/*
 * @(#)WorkflowProgressManager.java
 *
 * Copyright (c) 2003 DCIVision Ltd
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of DCIVision
 * Ltd ("Confidential Information").  You shall not disclose such Confidential
 * Information and shall use it only in accordance with the terms of the license
 * agreement you entered into with DCIVision Ltd.
 */
package com.dcivision.workflow.core;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobDataMap;

import com.dcivision.calendar.bean.CalendarRecord;
import com.dcivision.calendar.dao.CalendarRecordDAObject;
import com.dcivision.dms.dao.DmsDocumentDAObject;
import com.dcivision.dms.dao.DmsVersionDAObject;
import com.dcivision.form.bean.FormData;
import com.dcivision.form.bean.FormRecord;
import com.dcivision.form.bean.FormSubmission;
import com.dcivision.form.dao.FormDataElementDAObject;
import com.dcivision.form.dao.FormRecordDAObject;
import com.dcivision.form.dao.FormSubmissionDAObject;
import com.dcivision.framework.ApplicationContainer;
import com.dcivision.framework.ApplicationException;
import com.dcivision.framework.DataSourceFactory;
import com.dcivision.framework.ErrorConstant;
import com.dcivision.framework.GlobalConstant;
import com.dcivision.framework.MessageResourcesFactory;
import com.dcivision.framework.SessionContainer;
import com.dcivision.framework.SystemFunctionConstant;
import com.dcivision.framework.SystemParameterConstant;
import com.dcivision.framework.SystemParameterFactory;
import com.dcivision.framework.TextUtility;
import com.dcivision.framework.Utility;
import com.dcivision.framework.bean.AbstractBaseObject;
import com.dcivision.framework.bean.SysFunction;
import com.dcivision.framework.dao.SysFunctionDAObject;
import com.dcivision.framework.web.AbstractActionForm;
import com.dcivision.setup.core.HolidayManager;
import com.dcivision.user.bean.MtmUserGroupUserRole;
import com.dcivision.user.bean.UserDelegation;
import com.dcivision.user.bean.UserRecord;
import com.dcivision.user.dao.MtmUserGroupUserRoleDAObject;
import com.dcivision.user.dao.MtmUserRecordUserGroupDAObject;
import com.dcivision.user.dao.MtmUserRecordUserRoleDAObject;
import com.dcivision.user.dao.UserDelegationDAObject;
import com.dcivision.user.dao.UserRecordDAObject;
import com.dcivision.workflow.WorkflowErrorConstant;
import com.dcivision.workflow.bean.MtmWfProgressAccessForm;
import com.dcivision.workflow.bean.MtmWfProgressWfProgress;
import com.dcivision.workflow.bean.MtmWfStepNotificationRule;
import com.dcivision.workflow.bean.MtmWfStepNotificationTarget;
import com.dcivision.workflow.bean.MtmWorkflowDynamicUserActor;
import com.dcivision.workflow.bean.MtmWorkflowInterruptUserActor;
import com.dcivision.workflow.bean.MtmWorkflowProgressPriorityRule;
import com.dcivision.workflow.bean.MtmWorkflowProgressSystemObject;
import com.dcivision.workflow.bean.MtmWorkflowProgressUserRecord;
import com.dcivision.workflow.bean.MtmWorkflowRecordObjectLink;
import com.dcivision.workflow.bean.MtmWorkflowStepPriorityRule;
import com.dcivision.workflow.bean.MtmWorkflowStepUserActor;
import com.dcivision.workflow.bean.MtmWorkflowStepWorkflowStep;
import com.dcivision.workflow.bean.WorkflowAction;
import com.dcivision.workflow.bean.WorkflowProgress;
import com.dcivision.workflow.bean.WorkflowProgressAttachment;
import com.dcivision.workflow.bean.WorkflowRecord;
import com.dcivision.workflow.bean.WorkflowStep;
import com.dcivision.workflow.bean.WorkflowStepFormEquat;
import com.dcivision.workflow.dao.MtmWfProgressAccessFormDAObject;
import com.dcivision.workflow.dao.MtmWfProgressWfProgressDAObject;
import com.dcivision.workflow.dao.MtmWfStepFormRecordDAObject;
import com.dcivision.workflow.dao.MtmWfStepNotificationRuleDAObject;
import com.dcivision.workflow.dao.MtmWorkflowDynamicUserActorDAObject;
import com.dcivision.workflow.dao.MtmWorkflowInterruptUserActorDAObject;
import com.dcivision.workflow.dao.MtmWorkflowProgressPriorityRuleDAObject;
import com.dcivision.workflow.dao.MtmWorkflowProgressSystemObjectDAObject;
import com.dcivision.workflow.dao.MtmWorkflowProgressUserRecordDAObject;
import com.dcivision.workflow.dao.MtmWorkflowRecordObjectLinkDAObject;
import com.dcivision.workflow.dao.MtmWorkflowStepPriorityRuleDAObject;
import com.dcivision.workflow.dao.MtmWorkflowStepUserActorDAObject;
import com.dcivision.workflow.dao.MtmWorkflowStepWorkflowStepDAObject;
import com.dcivision.workflow.dao.WorkflowActionDAObject;
import com.dcivision.workflow.dao.WorkflowProgressAttachmentDAObject;
import com.dcivision.workflow.dao.WorkflowProgressDAObject;
import com.dcivision.workflow.dao.WorkflowRecordDAObject;
import com.dcivision.workflow.dao.WorkflowStepDAObject;
import com.dcivision.workflow.dao.WorkflowStepFormEquatDAObject;
import com.dcivision.workflow.web.ListWorkflowProgressForm;
import com.dcivision.workflow.web.MaintWorkflowProgressForm;
import com.dcivision.workflow.web.MaintWorkflowRecordForm;

/**
 * WorkflowProgressManager.java
 * 
 * This class is responsible for workflow progress operation.
 * 
 * @author Rollo Chan
 * @company DCIVision Limited
 * @creation date 06/10/2003
 * @version $Revision: 1.196.2.9 $
 */
public class WorkflowProgressManager {
  
  public static final String REVISION = "$Revision: 1.196.2.9 $";
  
  private static Object progressLock = new Object();
  
  protected Log log = LogFactory.getLog(this.getClass().getName());
  private SessionContainer sessionCon = null;
  private Connection dbConn = null;
  private int nextStepRoutineID = 0;
  private int numOfRoutine = 1;
  private boolean bAutoExecuteNextStep = false;
  private boolean bCreateNextWorkflowProgress = true;
  private String currentProgressStatus = WorkflowProgress.STATUS_GENERAL_ACCOMPLISHMENT;
  private HashMap assignStepMap = new HashMap();// For dynamic assign owner for this step
  private HashMap assignPriorityStepMap = new HashMap(); // For dynamic assign step's priority for this step
  private String autoRoutedToDefault = null; //For Form Value Driven Task
  
  /**
   * Constructor
   *
   * @param sessionContainer
   * @param conn
   */
  public WorkflowProgressManager(SessionContainer sessionContainer, Connection conn) {
    this.sessionCon = sessionContainer;
    this.dbConn = conn;
  }
  /**
   * Gets the next workflow step id.
   * @return  The next step routine ID
   */
  public int getNextStepRoutineID() {
    return this.nextStepRoutineID;
  }
  /**
   * Gets the number of possible routine.
   * @return The number of possible routine
   */
  public int getNumOfRoutine() {
    return this.numOfRoutine;
  }
  /**
   * Whether automatically execute the next step
   * @return True if next step should be automatically executed
   */
  public boolean isAutoExecuteNextStep() {
    return this.bAutoExecuteNextStep;
  }
  /**
   * Whether to create next step's progress object
   * @return True if next step's progress object should be created
   */
  public boolean isCreateNextWorkflowProgress() {
    return this.bCreateNextWorkflowProgress;
  }
  
  /**
   * For Form Value Driven Task, if throw a parse exception, then the workflow auto routed to default route.
   * @return String The route description
   * @see com.dcivision.workflow.bean.WorkflowProgress#FORM_EQUATION_ERROR
   * @see com.dcivision.workflow.bean.WorkflowProgress#ROUTED_TO_DEFAULT
   */
  public String getAutoRoutedToDefault(){
    return this.autoRoutedToDefault;
  }
  
  /**
   * Gets the status of the current workflow progress.
   * @return The current workflow progress status
   */
  public String getCurrentProgressStatus() {
    return this.currentProgressStatus;
  }
  
  /**
   * Gets the map that store the step id which need to dynamic assign workflow user.
   *
   */
  public HashMap getAssignStepMap() {
    return this.assignStepMap;
  }
  
  /**
   * Gets the map that store the step id which need to dynamic assign step priority.
   *
   */
  public HashMap getAssignPriorityStepMap() {
    return this.assignPriorityStepMap;
  }
  
  /**
   * Gets the workflow progress list by workflow track ID.
   *
   * @param trackID     Workflow track ID
   * @return            List of WorkflowProgress object
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.WorkflowProgress
   */
  public List getWorkflowProgressList(Integer trackID) throws ApplicationException {
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(sessionCon, dbConn);
    return progressDAO.getListByTrackID(trackID);
  }
  
  /**
   * Gets the workflow progress by workflow progress ID.
   *
   * @param progressID     Workflow progress ID
   * @return               The WorkflowProgress object
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.WorkflowProgress
   */
  public WorkflowProgress getWorkflowProgress(Integer progressID) throws ApplicationException {
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject (sessionCon, dbConn);
    return (WorkflowProgress) progressDAO.getObjectByID(progressID);
  }
  
  
  /**
   * Gets list of workflow progress attachment by workflow progress ID.
   *
   * @param progressID     Workflow progress ID
   * @return               List of WorkflowProgressAttachment object
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.WorkflowProgressAttachment
   */
  public List getWorkflowProgressAttachmentList(Integer progressID) throws ApplicationException {
    WorkflowProgressAttachmentDAObject attachmentDAO = new WorkflowProgressAttachmentDAObject (sessionCon, dbConn);
    List tempAttachmentList = attachmentDAO.getListByWorkflowProgressID(progressID);
    List attachmentList = removeUnexistWorkflowProgressAttachment(tempAttachmentList);
    return attachmentList;
  }

  /**
   * Gets the workflow progress attachment
   *
   * @param progressID     attachment id
   * @return               The WorkflowProgressAttachment object
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.WorkflowProgressAttachment
   */
  public WorkflowProgressAttachment getWorkflowProgressAttachment(Integer attachmentID) throws ApplicationException {
    WorkflowProgressAttachmentDAObject attachmentDAO = new WorkflowProgressAttachmentDAObject (sessionCon, dbConn);
    return (WorkflowProgressAttachment)attachmentDAO.getObjectByID(attachmentID);
  }
  
  /**
   * Gets the workflow progress attachment, with the content of attachment returned also
   *
   * @param progressID     attachment id
   * @return               The WorkflowProgressAttachment object
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.WorkflowProgressAttachment
   */
  public WorkflowProgressAttachment getWorkflowProgressAttachmentWithContent(Integer attachmentID) throws ApplicationException {
    WorkflowProgressAttachment progressAttachment = this.getWorkflowProgressAttachment(attachmentID);
    
    InputStream dataStream = this.getDataInputStreamByAttachment(progressAttachment);
    progressAttachment.setDataStream(dataStream);
    
    return progressAttachment;
  }
  
  /**
   * Gets the content of the given workflow progress attachment as InputStream.
   *
   * @param attachment     workflow progress attachment record
   * @return               the input stream of the workflow attachment file
   * @throws ApplicationException
   */
  public InputStream getDataInputStreamByAttachment(WorkflowProgressAttachment attachment) throws ApplicationException {
    InputStream dataStream = null;
    WorkflowOperationManager wfOperationManager = new WorkflowOperationManager(sessionCon, dbConn);
    
    String attachmentType = attachment.getAttachmentType();
    if (WorkflowProgressAttachment.ATTACHED_SINGLE_FILE.equals(attachmentType) || WorkflowProgressAttachment.ATTACHED_NEW_CHECK_IN_DOCUMENT.equals(attachmentType)) {
      WorkflowContentStoreHandler contentStoreHandler = null;
      try {
        contentStoreHandler = (WorkflowContentStoreHandler)Class.forName(SystemParameterFactory.getSystemParameter(SystemParameterConstant.WORKFLOW_CONTENT_STORE_CLASS)).newInstance();
      } catch (Exception e) {
        throw new ApplicationException(WorkflowErrorConstant.CONTENT_HANDLER_CLASS_NOT_FOUND);
      }
      contentStoreHandler.setProperty(WorkflowContentStoreHandler.FILE_NAME_PROPERTY, attachment.getExternalID());
      String attachmentRootFolder = wfOperationManager.getStorageLocation() + "/";
      contentStoreHandler.setProperty(WorkflowContentStoreHandler.FILE_PHYSICAL_PATH, attachmentRootFolder);
      dataStream = contentStoreHandler.readStream();
    }
    
    return dataStream;
  }
  
  public synchronized Boolean accomplishWorkflowProgressByUser(MaintWorkflowProgressForm progressForm) throws ApplicationException {
	return accomplishWorkflowProgressByUser(progressForm,null);
  }
  
  
  /**
   * Handles all the operations against a workflow progress by a user, including
   * checkout, complete, approve, reject, yes, no, cancel, etc. It will also check if
   * it is necessary to proceed to the next task by calling createNextWorkflowProgressesAfterCurrentProgress
   *
   * @param progressFrom   MaintWorkflowProgressForm which contains all the data needed
   *                       for the accomplishment of the workflow progress
   * @return               the result of the update
   * @throws ApplicationException
   * @see #checkFullfillWorkflowStepRules
   */
  public synchronized Boolean accomplishWorkflowProgressByUser(MaintWorkflowProgressForm progressForm,Integer autoTaskOwnerID) throws ApplicationException {
    synchronized(progressLock) {
      log.debug("-=-=-=-=-=-=-=-=-=-= accomplishWorkflowProgressByUser: -=-=-=-=-=-=-=-=-=-=-=");
      MtmWorkflowProgressUserRecordDAObject progressUserRecordDAO = new MtmWorkflowProgressUserRecordDAObject(sessionCon, dbConn);
      WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionCon, dbConn);
      WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(sessionCon, dbConn);
      WorkflowOperationManager wfOperationManager = new WorkflowOperationManager(sessionCon, dbConn);
      MtmWorkflowProgressSystemObjectDAObject progressObjectDAO = new MtmWorkflowProgressSystemObjectDAObject(sessionCon, dbConn);
      CalendarRecordDAObject calendarRecordDAObject = new CalendarRecordDAObject(sessionCon,dbConn);
      
      WorkflowProgress currentProgress = (WorkflowProgress) progressForm.getFormData();
      WorkflowProgress existProgress = (WorkflowProgress) progressDAO.getObjectByID(currentProgress.getID());
      
      String oldStatusInDB=existProgress.getStatus();
      
      WorkflowStep currentStep = (WorkflowStep) stepDAO.getObjectByID(existProgress.getWorkflowStepID());

      //get delegation User
      boolean isEmptyDelegated = Integer.valueOf("0").equals(currentProgress.getDelegateUserRecordID()) || currentProgress.getDelegateUserRecordID() == null || Integer.valueOf("-1").equals(currentProgress.getDelegateUserRecordID());
      Integer delegateUserRecordID = getDelegationUser(currentProgress,isEmptyDelegated);
      //delegation User end
      //get the operator
      //bill add for EIP-1709 start
      //MtmWorkflowStepUserActorDAObject mtmWorkflowStepUserActorDAO=new MtmWorkflowStepUserActorDAObject(sessionCon,dbConn);
      //List stepUserActorList=mtmWorkflowStepUserActorDAO.getListByWorkflowStepID(new Integer(progressForm.getWorkflowStepID()));
      Integer adminUserID=new Integer(0);
      Integer userRecordID;
      //if(Utility.isEmpty(stepUserActorList)){
      //userRecordID = getUserRecordID(adminUserID, currentProgress, isEmptyDelegated);   
      //}else{
      userRecordID = getUserRecordID(autoTaskOwnerID, currentProgress, isEmptyDelegated);  
      //}
      //bill add for EIP-1709 end
      
      //check the condition decide if can operation the progress...
      checkOperateStepCondition(progressUserRecordDAO, progressDAO, currentProgress, existProgress, currentStep, userRecordID);
      //check end
      
      if (WorkflowProgress.STATUS_CANCEL.equals(currentProgress.getStatus())
           || WorkflowProgress.STATUS_IN_PROGRESS.equals(currentProgress.getStatus())) {
            this.bCreateNextWorkflowProgress = false;
      } 
      
  	  
      Integer autoTaskUpdateUserID = getAutoTaskActorUser(currentProgress, currentStep);
      userRecordID=autoTaskUpdateUserID!=null?autoTaskUpdateUserID:userRecordID;
      // get the operator end
      
      String[] decision = getDecision(progressForm, currentProgress, currentStep);
      List tiggerTypeList = getCurrentNotifyTiggerList(currentProgress,currentStep,decision);
      
      //insert/update the progress operator info 
      MtmWorkflowProgressUserRecord progressUserRecord =getProgressUserRecordUser(progressForm, progressUserRecordDAO, currentProgress, delegateUserRecordID, userRecordID, decision); 
      
      if(WorkflowProgress.STATUS_CANCEL.equals(currentProgress.getStatus())){
      	cancelTask(progressForm, wfOperationManager, progressObjectDAO, currentProgress, currentStep, progressUserRecord);
      	return (new Boolean(false));
      }
      
      createNotifacation(existProgress, oldStatusInDB, tiggerTypeList);
      
      updateCurrentUserCalendar(calendarRecordDAObject, currentProgress);
      
      // check with the workflow step rule, decided the progress has gained enough action from user for proceed or not
      boolean proceedToNextProgress = checkFullfillWorkflowStepRules(currentStep, currentProgress);
      
      // swap and update workflow progress sequence number for pending workflow progress
      List pendingProgressList = progressDAO.getPendingProgressListByTrackID(existProgress.getTrackID());
      Integer swapSeqNo = swapAndUpdateWorkflowProgressSequence(progressForm, progressDAO, currentProgress, existProgress, proceedToNextProgress, pendingProgressList);
      
      if (proceedToNextProgress) {//operate the progress:submit/complete/check out and so on
        currentProgress = proceedNextProgress(progressForm, autoTaskOwnerID, progressDAO, calendarRecordDAObject, currentProgress, existProgress, autoTaskUpdateUserID, pendingProgressList, swapSeqNo);
      } else {//if can not proceedToNextProgress,set this progress status = 'pending'//I think should hold the existProgress status not changed TC
        unProceedNextProgress(progressForm, progressDAO, currentProgress, existProgress);
      }
      
      //create next workflow progress
      createNextWorkflowProgress(progressForm, currentProgress, currentStep, decision, proceedToNextProgress);
      
      handleWorkflowAttachments(progressForm, wfOperationManager, progressObjectDAO, currentProgress, progressUserRecord);
      log.debug("-=-=-=-=-=-=-=-=-=-= accomplishWorkflowProgressByUser end!!-=-=-=-=-=-=-=-=-=-=-=");
      return new Boolean(proceedToNextProgress);
    }
    
  }
  private void handleWorkflowAttachments(MaintWorkflowProgressForm progressForm, WorkflowOperationManager wfOperationManager, MtmWorkflowProgressSystemObjectDAObject progressObjectDAO, WorkflowProgress currentProgress, MtmWorkflowProgressUserRecord progressUserRecord) throws ApplicationException {
    List tmpList = progressForm.getAttachmentList();
    if (tmpList!=null) {
      log.debug("-=-=-=-=-=-=-=-=-=-= attachment list size: "+ tmpList.size() + " -=-=-==-=-=-=-=-=-=-=");
      MtmWorkflowProgressSystemObjectDAObject mtmWorkflowProgressSystemObjectDAO = new MtmWorkflowProgressSystemObjectDAObject(sessionCon, dbConn);
      
      /**
       * @ToDo To get the MtmWorkflowProgressSystemObject by trackID & objectID 
       */
      MtmWorkflowProgressSystemObject progressObj = null;
      List objectList = mtmWorkflowProgressSystemObjectDAO.getListByTrackID(currentProgress.getTrackID());
      if(!Utility.isEmpty(objectList)){
        progressObj = (MtmWorkflowProgressSystemObject)objectList.get(0);  
      }
      //MtmWorkflowProgressSystemObject progressObj = (MtmWorkflowProgressSystemObject)mtmWorkflowProgressSystemObjectDAO.getObjectByTrackID(currentProgress.getTrackID());
      JobDataMap dataMap=null;
      
      if (progressObj!=null) {
        dataMap = new JobDataMap((Map) progressObj.getParameterObject());
        Map objParameter = (Map) dataMap.get("OBJECT_PARAMETER");
        if (objParameter == null) {
          objParameter = new HashMap();
        }
        objParameter.put("DOC_CHECKIN_IPADDRESS", this.sessionCon.getUserIPAddress());
        objParameter.put("DOC_CHECKIN_SESSIONID", this.sessionCon.getSessionID());
        
        dataMap.put("OBJECT_PARAMETER", objParameter);
        progressObj.setParameterObject(dataMap.getWrappedMap());
        progressObjectDAO.updateObject(progressObj);
      }
    }
    wfOperationManager.createWorkflowProgressAttachments(progressForm.getAttachmentList(), progressUserRecord.getID());
  }
  private void createNextWorkflowProgress(MaintWorkflowProgressForm progressForm, WorkflowProgress currentProgress, WorkflowStep currentStep, String[] decision, boolean proceedToNextProgress) throws ApplicationException {
    if (this.bCreateNextWorkflowProgress) {
      String[] nextStepDueDateArr = null;
      if(!Utility.isEmpty(progressForm.getNextStepDueDates())){
        nextStepDueDateArr = TextUtility.splitString(progressForm.getNextStepDueDates(),";");
      }
      //
      this.createNextWorkflowProgressesAfterCurrentProgress(currentStep, currentProgress, decision, proceedToNextProgress, nextStepDueDateArr );
      
      //set dynamic assign priority.
      if(!Utility.isEmpty(progressForm.getPriorityRuleListValue()) && !this.getAssignPriorityStepMap().isEmpty() ){
        this.insertProgressPriorityRule(this.getAssignPriorityStepMap(), progressForm.getPriorityRuleListValue());
      }
      
      // Check next step(s) is allow dynamic assign priority.
      if(!this.getAssignPriorityStepMap().isEmpty() ){
        this.handelScheduleJob(this.getAssignPriorityStepMap());
      }
    }
  }
  private void unProceedNextProgress(MaintWorkflowProgressForm progressForm, WorkflowProgressDAObject progressDAO, WorkflowProgress currentProgress, WorkflowProgress existProgress) throws ApplicationException {
    existProgress.setNextStepDueDates(progressForm.getNextStepDueDates());
    if (WorkflowProgress.STATUS_CANCEL.equals(currentProgress.getStatus())||WorkflowProgress.STATUS_IN_PROGRESS.equals(currentProgress.getStatus())) {
      existProgress.setStatus(currentProgress.getStatus());
    }
    //simon_add_S
    //if currentProgress states is Complete, but the proceedToNextProgress is false ,must set the existProgress' states is pending
    if (WorkflowProgress.STATUS_GENERAL_ACCOMPLISHMENT.equals(currentProgress.getStatus()) ) {
      existProgress.setStatus(WorkflowProgress.STATUS_PENDING);
    }
    //simon_add_E
    progressDAO.updateObject(existProgress);
  }
  private WorkflowProgress proceedNextProgress(MaintWorkflowProgressForm progressForm, Integer autoTaskOwnerID, WorkflowProgressDAObject progressDAO, CalendarRecordDAObject calendarRecordDAObject, WorkflowProgress currentProgress, WorkflowProgress existProgress, Integer autoTaskUpdateUserID, List pendingProgressList, Integer swapSeqNo) throws ApplicationException {
    // if task is completed ,check carendar Record table ,if have active record for this progressid,
    // update the record
    List activeCalendarRecordList = calendarRecordDAObject.getListByObjectIDAndObjectTypeNotCurrentUser(currentProgress.getID(), CalendarRecord.OBJECT_TYPE_WORKFLOW,  sessionCon.getUserRecordID());
    for (int h = 0; !Utility.isEmpty(activeCalendarRecordList) && h < activeCalendarRecordList.size(); h++ ) {
      CalendarRecord calendarRecord = (CalendarRecord)activeCalendarRecordList.get(h);
      calendarRecord.setStatus(GlobalConstant.TRUE);
      calendarRecordDAObject.updateObject(calendarRecord);
    }
    
    //* pendingProgressList equals to zero means it is an inital start
    if (pendingProgressList.size() == 0) {
      List finishedProgressList = progressDAO.getFinishedProgressListByTrackID(currentProgress.getTrackID());
      swapSeqNo = new Integer(finishedProgressList.size());
    }
    existProgress.setSeqNo(swapSeqNo);
    existProgress.setStatus(currentProgress.getStatus());
    existProgress.setPostProgress(currentProgress.getPostProgress());
    existProgress.setNextStepDueDates(progressForm.getNextStepDueDates());
    existProgress.setPostProgress(currentProgress.getPostProgress());
    
    //set the AutoTask Owner 
    if (autoTaskUpdateUserID != null) {
      existProgress.setUpdaterID(autoTaskOwnerID);
    }
    //currentProgress.setUpdaterID(); //* TO FIX: FORGET WHY HAVE TO SET UPDATER ID, LEAVE IT AND FIX LATER
    
    //To handle the submit status when the mutil-form start the flow.
    if(swapSeqNo.equals(new Integer(1))){
      existProgress.setStatus(WorkflowProgress.STATUS_SUBMIT);  
    }
    currentProgress = (WorkflowProgress)progressDAO.updateObject(existProgress);
    
    List completeList=new ArrayList();
    completeList.add( MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_COMPLETED);
    this.createNotificationWhenTaskUpdated(existProgress, completeList);
    return currentProgress;
  }
  private Integer swapAndUpdateWorkflowProgressSequence(MaintWorkflowProgressForm progressForm, WorkflowProgressDAObject progressDAO, WorkflowProgress currentProgress, WorkflowProgress existProgress, boolean proceedToNextProgress, List pendingProgressList) throws ApplicationException {
    Integer currentProgSeq = existProgress.getSeqNo();
    Integer swapSeqNo=null;
    for (int i=0; i<pendingProgressList.size(); i++) {
      WorkflowProgress pendingProgress = (WorkflowProgress) pendingProgressList.get(i);
      if (i==0) {
        swapSeqNo = pendingProgress.getSeqNo();
        pendingProgress.setSeqNo(currentProgSeq);
      }
      
      //* if the pending progress is not equals to the current progress, update it to record down the new updater and update time
      //* Current progress is not update here, as it will update just after this part
      if (!pendingProgress.getID().equals(existProgress.getID()) && proceedToNextProgress) {
        progressDAO.updateObject(pendingProgress);
      }
    }
    
    if (this.checkAssignSecondStepOwner(currentProgress) || this.checkAssignNextStepOwner(progressForm)) {
      currentProgress.setPostProgress(WorkflowProgress.STATUS_POST_PROGRESS);
    }
    return swapSeqNo;
  }
  private void updateCurrentUserCalendar(CalendarRecordDAObject calendarRecordDAObject, WorkflowProgress currentProgress) throws ApplicationException {
    if(!WorkflowProgress.STATUS_IN_PROGRESS.equals(currentProgress.getStatus())){
      List calendarRecordList = calendarRecordDAObject.getListByObjectIDAndObjectTypeAndCreatorID(currentProgress.getID(), CalendarRecord.OBJECT_TYPE_WORKFLOW, sessionCon.getUserRecordID());
      for (int h = 0; !Utility.isEmpty(calendarRecordList) && h < calendarRecordList.size(); h++ ) {
        CalendarRecord calendarRecord = (CalendarRecord)calendarRecordList.get(h);
        calendarRecord.setStatus(GlobalConstant.TRUE);
        calendarRecordDAObject.updateObject(calendarRecord);
      }
    }
  }
  private void createNotifacation(WorkflowProgress existProgress, String oldStatusInDB, List tiggerTypeList) throws ApplicationException {
    if(!WorkflowProgress.STATUS_IN_PROGRESS.equals(oldStatusInDB)){
      this.createNotificationWhenTaskUpdated(existProgress, tiggerTypeList);
    }
  }
  private MtmWorkflowProgressUserRecord getProgressUserRecordUser(MaintWorkflowProgressForm progressForm, MtmWorkflowProgressUserRecordDAObject progressUserRecordDAO, WorkflowProgress currentProgress, Integer delegateUserRecordID, Integer userRecordID, String[] decision) throws ApplicationException {
    MtmWorkflowProgressUserRecord progressUserRecord=new MtmWorkflowProgressUserRecord();
    if (Utility.isEmpty(decision)) {
      progressUserRecord = getProgressUserRecord(progressForm, progressUserRecordDAO, currentProgress, delegateUserRecordID, userRecordID);
    } else{
      progressUserRecord = getProgressUserRocordForDecision(progressForm, progressUserRecordDAO, currentProgress, delegateUserRecordID, userRecordID, decision, progressUserRecord);
    }
    return progressUserRecord;
  }
  private MtmWorkflowProgressUserRecord getProgressUserRocordForDecision(MaintWorkflowProgressForm progressForm, MtmWorkflowProgressUserRecordDAObject progressUserRecordDAO, WorkflowProgress currentProgress, Integer delegateUserRecordID, Integer userRecordID, String[] decision, MtmWorkflowProgressUserRecord progressUserRecord) throws ApplicationException {
    // for each of the decision made, add a MTM_WORKFLOW_PROGRESS_USER_RECORD record
    for (int i=0; i<decision.length; i++) {
      progressUserRecord = progressUserRecordDAO.getPendingObjectByWorkflowProgressIDUserRecordID(currentProgress.getID(), userRecordID);
      progressUserRecord.setWorkflowProgressID(currentProgress.getID());
      progressUserRecord.setUserRecordID(userRecordID);
      progressUserRecord.setDelegateUserRecordID(delegateUserRecordID);
      //if(WorkflowProgress.STATUS_IN_PROGRESS.equals(currentProgress.getStatus())){//simon_del
        progressUserRecord.setWorkflowAction(currentProgress.getStatus());
      //} else {//simon_del
        //progressUserRecord.setWorkflowAction(decision[i]);//simon_del
        progressUserRecord.setOperationParameter(decision[i]);//simon_add
      //}//simon_del
      progressUserRecord.setReason(progressForm.getReason());
      
      log.debug("progress user record: ID="+progressUserRecord.getID()+", progress id="+progressUserRecord.getWorkflowProgressID()+", user record id" + progressUserRecord.getUserRecordID());
      if (Utility.isEmpty(progressUserRecord.getID()) ) {
        progressUserRecord = (MtmWorkflowProgressUserRecord) progressUserRecordDAO.insertObject(progressUserRecord);
      } else {
        progressUserRecord = (MtmWorkflowProgressUserRecord) progressUserRecordDAO.updateObject(progressUserRecord);
      }
      
      // TODO: For multiply transition just need one actor record, record the decision info in another way???
      break;
    }
    return progressUserRecord;
  }
  private void cancelTask(MaintWorkflowProgressForm progressForm, WorkflowOperationManager wfOperationManager, MtmWorkflowProgressSystemObjectDAObject progressObjectDAO, WorkflowProgress currentProgress, WorkflowStep currentStep, MtmWorkflowProgressUserRecord progressUserRecord) throws ApplicationException {
    wfOperationManager.stopWorkflowTrack(currentProgress,currentStep);
     List tmpList = progressForm.getAttachmentList();
     if (tmpList!=null) {
       log.debug("-=-=-=-=-=-=-=-=-=-= attachment list size: "+ tmpList.size() + " -=-=-==-=-=-=-=-=-=-=");
       MtmWorkflowProgressSystemObjectDAObject mtmWorkflowProgressSystemObjectDAO = new MtmWorkflowProgressSystemObjectDAObject(sessionCon, dbConn);
       
       /**
        * @ToDo To get the MtmWorkflowProgressSystemObject by trackID & objectID 
        */
       MtmWorkflowProgressSystemObject progressObj = null;
       List objectList = mtmWorkflowProgressSystemObjectDAO.getListByTrackID(currentProgress.getTrackID());
       if(!Utility.isEmpty(objectList)){
         progressObj = (MtmWorkflowProgressSystemObject)objectList.get(0);  
       }
       //MtmWorkflowProgressSystemObject progressObj = (MtmWorkflowProgressSystemObject)mtmWorkflowProgressSystemObjectDAO.getObjectByTrackID(currentProgress.getTrackID());
       JobDataMap dataMap=null;
       
       if (progressObj!=null) {
         dataMap = new JobDataMap((Map) progressObj.getParameterObject());
         Map objParameter = (Map) dataMap.get("OBJECT_PARAMETER");
         if (objParameter == null) {
           objParameter = new HashMap();
         }
         objParameter.put("DOC_CHECKIN_IPADDRESS", this.sessionCon.getUserIPAddress());
         objParameter.put("DOC_CHECKIN_SESSIONID", this.sessionCon.getSessionID());
         
         dataMap.put("OBJECT_PARAMETER", objParameter);
         progressObj.setParameterObject(dataMap.getWrappedMap());
         progressObjectDAO.updateObject(progressObj);
       }
     }
     wfOperationManager.createWorkflowProgressAttachments(progressForm.getAttachmentList(), progressUserRecord.getID());
     log.debug("-=-=-=-=-=-=-=-=-=-= accomplishWorkflowProgressByUser end!!-=-=-=-=-=-=-=-=-=-=-=");
  }
  private MtmWorkflowProgressUserRecord getProgressUserRecord(MaintWorkflowProgressForm progressForm, MtmWorkflowProgressUserRecordDAObject progressUserRecordDAO, WorkflowProgress currentProgress, Integer delegateUserRecordID, Integer userRecordID) throws ApplicationException {
    MtmWorkflowProgressUserRecord progressUserRecord;
    progressUserRecord = progressUserRecordDAO.getPendingObjectByWorkflowProgressIDUserRecordID(currentProgress.getID(), userRecordID);
    progressUserRecord.setWorkflowProgressID(currentProgress.getID());
    progressUserRecord.setUserRecordID(userRecordID);
    progressUserRecord.setDelegateUserRecordID(delegateUserRecordID);
    progressUserRecord.setWorkflowAction(currentProgress.getStatus());
    progressUserRecord.setReason(progressForm.getReason());
    log.debug("progress user record: ID="+progressUserRecord.getID()+", progress id="+progressUserRecord.getWorkflowProgressID()+", user record id" + progressUserRecord.getUserRecordID());
    if (Utility.isEmpty(progressUserRecord.getID()) ) {
      progressUserRecord = (MtmWorkflowProgressUserRecord) progressUserRecordDAO.insertObject(progressUserRecord);
    } else {
      progressUserRecord = (MtmWorkflowProgressUserRecord) progressUserRecordDAO.updateObject(progressUserRecord);
    }
    return progressUserRecord;
  }
  private Integer getAutoTaskActorUser(WorkflowProgress currentProgress, WorkflowStep currentStep) throws ApplicationException {
    boolean isAutoTask = WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(currentStep.getActionType());
    Integer autoTaskUpdateUserID=null;
    //administrator user ID 
    Integer adminUserID=new Integer(0);//bill add for EIP-1709 
    if (isAutoTask){
      Integer tmpuserRecordID = getAutoTaskActorUser(currentProgress);
      if (tmpuserRecordID!=null){
        autoTaskUpdateUserID = tmpuserRecordID;
        //userRecordID=tmpuserRecordID;
      }else{
    	//this auto task not hava owner,so assign administrator of it
    	autoTaskUpdateUserID=adminUserID;//bill add for EIP-1709
      }
    }
    return autoTaskUpdateUserID;
  }
  private Integer getUserRecordID(Integer autoTaskOwnerID, WorkflowProgress currentProgress, boolean isEmptyDelegated) {
    Integer userRecordID;
    if(isEmptyDelegated ){
      // The task is processed by the original person
      userRecordID = sessionCon.getUserRecordID();
//          delegateUserRecordID = null;
    } else {
      // If the task is processed by delegated user,
      // swap the userRecordID and delegateUserRecordID
      userRecordID = currentProgress.getDelegateUserRecordID();
//          delegateUserRecordID = sessionCon.getUserRecordID();
    }
    
    // if current workflow is start by autoTask
    if (autoTaskOwnerID!=null){
      userRecordID=autoTaskOwnerID;
    }
    return userRecordID;
  }
  private Integer getDelegationUser(WorkflowProgress currentProgress,boolean isEmptyDelegated) {
    Integer delegateUserRecordID = null;
    if(isEmptyDelegated ){
      // The task is processed by the original person
     // userRecordID = sessionCon.getUserRecordID();
        delegateUserRecordID = null;
    } else {
      // If the task is processed by delegated user,
      // swap the userRecordID and delegateUserRecordID
     // userRecordID = currentProgress.getDelegateUserRecordID();
        delegateUserRecordID = sessionCon.getUserRecordID();
    }
    return delegateUserRecordID;
  }
  

  
  private String[] getDecision(MaintWorkflowProgressForm progressForm, WorkflowProgress currentProgress, WorkflowStep currentStep) throws ApplicationException {
    String[] decision = progressForm.getDecision();
    log.debug("decision == null? "+(decision==null));
    
    //if current step is Form Value Driven Task, then gain the possible decision points.
    if (WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(currentStep.getActionType())) {
      decision = this.getDecisionPointForFormValueDrivenTaskByWorkflowStepIDTrackID(currentStep.getID(), currentProgress.getTrackID());
    }
    return decision;
  }
  private void checkOperateStepCondition(MtmWorkflowProgressUserRecordDAObject progressUserRecordDAO, WorkflowProgressDAObject progressDAO, WorkflowProgress currentProgress, WorkflowProgress existProgress, WorkflowStep currentStep, Integer userRecordID) throws ApplicationException {
    List userList=progressUserRecordDAO.getCompletedUserProcessRecordByWorkflowProgressID(currentProgress.getID());
    WorkflowProgressActionTakerCheckerManager progressCheckManager = new WorkflowProgressActionTakerCheckerManager(sessionCon,dbConn);
    /* 	if(WorkflowProgress.STATUS_CANCEL.equals(currentProgress.getStatus())){
          	wfOperationManager.stopWorkflowTrack(currentProgress,currentStep);
          	return (new Boolean(false));
    	}else{*///sliever.hu.update
    		if((GlobalConstant.TRUE).equals(((WorkflowProgress)progressDAO.getObjectByID(existProgress.getID())).getCancelFlag())){
    			throw new ApplicationException(ErrorConstant.WORKFLOW_TRACK_ALREADY_STOPPED );
    		}
    		
        	if(!Utility.isEmpty(userList)){
        		for(int j=0;j<userList.size();j++){
        			MtmWorkflowProgressUserRecord mtmProgressUser=(MtmWorkflowProgressUserRecord)userList.get(j);
        			if((mtmProgressUser.getUserRecordID()).equals(userRecordID)){
        				throw new ApplicationException(ErrorConstant.WORKFLOW_TASK_ALREADY_COMPLETED );
        			}
        		}
        	}
        		if(this.checkFullfillWorkflowStepRules(currentStep,currentProgress)){
        			throw new ApplicationException(ErrorConstant.WORKFLOW_TASK_ALREADY_COMPLETED);
        		}
    //	}//sliever.hu.update

            //------check something here---
            progressCheckManager.checkIfThisUserCanOperationThisTask(currentProgress,userRecordID);
            /*//check previousTaskActionTakersSupervisor,if not pass the check ,this method will throw exception
            checkPreviousTaskActionTakersSupervisorWithWriterPermission(currentStep.getID());
            //check someStepTaskActionTakersSupervisor,if not pass the check ,this method will throw exception
            checkSomeStepActionTakersSupervisorWithWritePermission(currentStep.getID());*/
            //------end--------------------     
  }
  
  private List getCurrentNotifyTiggerList(WorkflowProgress currentProgress,WorkflowStep currentStep, String[] decision) {
    List tiggerList=new ArrayList();
    if(WorkflowProgress.STATUS_IN_PROGRESS.equals(currentProgress.getStatus())){
       tiggerList.add(MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_CHECK_OUT_BY_USER);
    } else {
      tiggerList.add(MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_COMPLETED_BY_USER);
    }
    
    tiggerList.add(getCurrentBranchRule(currentProgress.getStatus()));
    
    if (!Utility.isEmpty(decision) && (WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(currentStep.getActionType()))){
      for (int i = 0; i < decision.length; i++) {
        tiggerList.add(decision[i]);
      }
    }
    
    return tiggerList;
  }

  private String getCurrentBranchRule(String progressStatus) {
    if (Utility.isEmpty(progressStatus)){
      return null;
    }
    
    if (WorkflowProgress.STATUS_APPROVE.equals(progressStatus)){
      return MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_APPROVED;
    }else if (WorkflowProgress.STATUS_REJECT.equals(progressStatus)){
      return MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_REJECT;
    }else if (WorkflowProgress.STATUS_YES.equals(progressStatus)){
      return MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_YES;
    }else if (WorkflowProgress.STATUS_NO.equals(progressStatus)){
      return MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_NO;
    }
    return null;
  }
  
  /**
   * Handles routing the given workflow progress to the given workflow step.
   *
   * @param workflowProgress The workflow progress to be routed
   * @param workflowStep The workflow step to route to
   * @throws ApplicationException
   */
  public void routeToOtherStep(WorkflowProgress workflowProgress, WorkflowStep workflowStep) throws ApplicationException {
    log.debug("route to other workflow step");
    
    MtmWorkflowProgressUserRecordDAObject progressUserRecordDAO = new MtmWorkflowProgressUserRecordDAObject(sessionCon, dbConn);
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(sessionCon, dbConn);
    WorkflowOperationManager wfOperationManager = new WorkflowOperationManager(sessionCon, dbConn);
    WorkflowRetrievalManager wfRetrievalManager = new WorkflowRetrievalManager(sessionCon, dbConn);
    
    List pendingProgressUserList = progressUserRecordDAO.getPendingObjectListByWorkflowProgressID(workflowProgress.getID());
    MtmWorkflowProgressUserRecord tmpProgressUser = new MtmWorkflowProgressUserRecord();
    if (pendingProgressUserList.size() == 0) {
      tmpProgressUser.setWorkflowProgressID(workflowProgress.getID());
      tmpProgressUser.setUserRecordID(sessionCon.getUserRecordID());
      tmpProgressUser.setWorkflowAction(WorkflowProgress.STATUS_NO_RESPONSE);
      tmpProgressUser.setReason("The step is routed to another step due to no response for a long time");
      tmpProgressUser = (MtmWorkflowProgressUserRecord) progressUserRecordDAO.insertObject(tmpProgressUser);
    } else {
      for (int i=0; i<pendingProgressUserList.size(); i++) {
        tmpProgressUser = (MtmWorkflowProgressUserRecord) pendingProgressUserList.get(i);
        tmpProgressUser.setUserRecordID(sessionCon.getUserRecordID());
        tmpProgressUser.setWorkflowAction(WorkflowProgress.STATUS_NO_RESPONSE);
        tmpProgressUser.setReason("The step is routed to another step due to no response for a long time");
        tmpProgressUser = (MtmWorkflowProgressUserRecord) progressUserRecordDAO.updateObject(tmpProgressUser);
      }
    }
    
    // swap and update workflow progress sequence number for pending workflow progress
    List pendingProgressList = progressDAO.getPendingProgressListByTrackID(workflowProgress.getTrackID());
    Integer currentProgSeq = workflowProgress.getSeqNo();
    Integer swapSeqNo = null;
    
    for (int i=0; i<pendingProgressList.size(); i++) {
      WorkflowProgress pendingProgress = (WorkflowProgress) pendingProgressList.get(i);
      if (i==0) {
        swapSeqNo = pendingProgress.getSeqNo();
        pendingProgress.setSeqNo(currentProgSeq);
      }
      
      //* if the pending progress is not equals to the current progress, update it to record down the new updater and update time
      //* Current progress is not update here, as it will update just after this part
      if (!pendingProgress.getID().equals(workflowProgress.getID())) {
        progressDAO.updateObject(pendingProgress);
      }
    }
    
    //* pendingProgressList equals to zero means it is an inital start
    if (pendingProgressList.size() == 0) {
      List finishedProgressList = progressDAO.getFinishedProgressListByTrackID(workflowProgress.getTrackID());
      swapSeqNo = new Integer(finishedProgressList.size());
    }
    workflowProgress.setSeqNo(swapSeqNo);
    workflowProgress.setStatus(WorkflowProgress.STATUS_NO_RESPONSE);
    //currentProgress.setUpdaterID(); //* TO FIX: FORGET WHY HAVE TO SET UPDATER ID, LEAVE IT AND FIX LATER
    workflowProgress = (WorkflowProgress)progressDAO.updateObject(workflowProgress);
    
    // create next workflow progresses based on the route to next step information
    int seqNo = progressDAO.getListByTrackID(workflowProgress.getTrackID()).size() + 1;
    WorkflowStep childStep = (WorkflowStep) wfRetrievalManager.getWorkflowStepInfo(workflowStep.getOnNoResponseNextStepID(), null);
    
    WorkflowProgress newProgress = new WorkflowProgress();
    newProgress.setTrackID(workflowProgress.getTrackID());
    newProgress.setWorkflowStepID(childStep.getID());
    newProgress.setSeqNo(new Integer(seqNo));
    newProgress.setCancelFlag(GlobalConstant.FALSE);
    newProgress.setCompleteFlag(GlobalConstant.FALSE);
    newProgress.setStatus(WorkflowProgress.STATUS_PENDING);
    newProgress.setProgressPriority(childStep.getPriority());
    newProgress.setWorkflowStepSource(childStep); //* set what step information is related to this progress
    newProgress =  wfOperationManager.createWorkflowProgress(newProgress, false);
    newProgress.setWorkflowStepSource(childStep);
    if (WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(childStep.getActionType())) {
      MaintWorkflowProgressForm progressForm = new MaintWorkflowProgressForm();
      newProgress.setStatus(childStep.getActionType().equals(WorkflowStep.ACTION_TYPE_APPROVAL)?WorkflowProgress.STATUS_APPROVE:WorkflowProgress.STATUS_GENERAL_ACCOMPLISHMENT);
      progressForm.setFormData(newProgress);
      this.accomplishWorkflowProgressByUser(progressForm);
    }
    log.debug("end route to other workflow step");
  }
  
  
  /**
   * Checks if the workflow rule for the current workflow step is fullfilled yet.
   * (NOTE: currently just have one method for checking rule. Should support the use
   * of different rules later. All workflow rule checking should handle through this function.
   * If after due action is auto-complete type, default to 'fullfill' after auto-completed.
   *
   * @param currentStep       the current workflow step
   * @param currentProgress   the current workflow progress
   * @return                  return true if the rule is fullfilled
   * @throws ApplicationException
   */
  protected boolean checkFullfillWorkflowStepRules(WorkflowStep currentStep, WorkflowProgress currentProgress) throws ApplicationException {
    boolean fullfilledRules = false;
    
    MtmWorkflowProgressUserRecordDAObject progressUserRecordDAO = new MtmWorkflowProgressUserRecordDAObject(sessionCon, dbConn);
    MtmWorkflowDynamicUserActorDAObject dynamicUserDAO = new MtmWorkflowDynamicUserActorDAObject(sessionCon, dbConn);
    WorkflowRetrievalManager wfRetrievalManager = new WorkflowRetrievalManager(sessionCon, dbConn);
    
    // get the list of existing finished workflow progress user record
    List progressUserRecordList = progressUserRecordDAO.getFinishedProgressListByWorkflowProgressID(currentProgress.getID());
    int listSize = progressUserRecordList.size();
    
    currentStep = (WorkflowStep) wfRetrievalManager.getWorkflowStepInfo(currentStep.getID(), null);
    
    int workflowRule = 0;
    
    if("*".equals(currentStep.getWorkflowRule())){
      List assignedUserList = null;
      log.debug("currentStep.getActorType()="+currentStep.getActorType());
      if (MtmWorkflowStepUserActor.ACTOR_TYPE_RECURSIVE_RUNTIME_ASSIGN.equals(currentStep.getAssignActorType())
          || MtmWorkflowStepUserActor.ACTOR_TYPE_RUNTIME_ASSIGN.equals(currentStep.getAssignActorType()) ){
        assignedUserList = dynamicUserDAO.getDynamicUserIDListByWorkflowProgressID(currentProgress.getID());
      } else {
        assignedUserList = wfRetrievalManager.getUserListForStepAssignment(currentStep.getID());
      }
      workflowRule = assignedUserList.size();
    } else {
      workflowRule = TextUtility.parseInteger(currentStep.getWorkflowRule());
    }
    
    //* for dynamic assign
    //    outer: if (MtmWorkflowStepUserActor.ACTOR_TYPE_RECURSIVE_RUNTIME_ASSIGN.equals(currentStep.getActorType())
    //        || "*".equals(currentStep.getWorkflowRule()) ) {//MtmWorkflowStepUserActor.ACTOR_TYPE_RUNTIME_ASSIGN.equals(currentStep.getActorType()) ||
    //      fullfilledRules = true;
    //
    //      if (MtmWorkflowStepUserActor.ACTOR_TYPE_RECURSIVE_RUNTIME_ASSIGN.equals(currentStep.getActorType())
    //          || MtmWorkflowStepUserActor.ACTOR_TYPE_RUNTIME_ASSIGN.equals(currentStep.getActorType()) ){
    //        if(Utility.isEmpty(progressUserRecordList) || progressUserRecordList.size() < workflowRule){
    //          fullfilledRules = false;
    //        }
    //      } else {//For not dynamic assign owner at runtime case.
    //log.debug("for not dynamic assign, rule=*, list size = "+listSize+", workflowRule="+workflowRule);
    //        if(listSize < workflowRule){
    //          fullfilledRules = false;
    //        }
    //      }
    //      log.debug("fullfilledRules: "+fullfilledRules);
    //    } else
    outer: {
      if (workflowRule == 0) {
        // It is impossible to have rule equals 0, shold be avoided in the workflow setup stage
        // anyway if get into this case, can't proceed and need to throw an ApplicationException
        throw new ApplicationException(WorkflowErrorConstant.INVALID_WORKFLOW_RULE_STATUS);
      }
      
      if (WorkflowStep.ACTION_TYPE_APPROVAL.equals(currentStep.getActionType())) {
        int approved=0;
        int rejected=0;
        for (int i=0; i<progressUserRecordList.size(); i++) {
          MtmWorkflowProgressUserRecord progressUserRecord = (MtmWorkflowProgressUserRecord) progressUserRecordList.get(i);
          if ( progressUserRecord.getUserRecordID().intValue() ==0 ) {
            fullfilledRules = true;
            break outer;
          } else if (WorkflowProgress.STATUS_REJECT.equals(progressUserRecord.getWorkflowAction())) {
            rejected++;
          } else if (WorkflowProgress.STATUS_APPROVE.equals(progressUserRecord.getWorkflowAction())) {
            approved++;
          }
        }
        
        if (WorkflowProgress.STATUS_REJECT.equals(currentProgress.getStatus())) {
          if (rejected >= workflowRule) {
            fullfilledRules = true;
          }
        } else {
          if (approved >= workflowRule) {
            fullfilledRules = true;
          }
        }
        
      } else if (WorkflowStep.ACTION_TYPE_BOOLEAN.equals(currentStep.getActionType())) {
        int yes = 0;
        int no = 0;
        for (int i=0; i<progressUserRecordList.size(); i++) {
          MtmWorkflowProgressUserRecord progressUserRecord = (MtmWorkflowProgressUserRecord) progressUserRecordList.get(i);
          if ( progressUserRecord.getUserRecordID().intValue() ==0 ) {
            fullfilledRules = true;
            break outer;
          } else if (WorkflowProgress.STATUS_NO.equals(progressUserRecord.getWorkflowAction())) {
            no++;
          } else if (WorkflowProgress.STATUS_YES.equals(progressUserRecord.getWorkflowAction())) {
            yes++;
          }
        }
        
        if (WorkflowProgress.STATUS_NO.equals(currentProgress.getStatus())) {
          if (no >= workflowRule) {
            fullfilledRules = true;
          }
        } else {
          if (yes >= workflowRule) {
            fullfilledRules = true;
          }
        }
        //  Added by Lun to handle the decision point for multiple owner
      } else if (!Utility.isEmpty(currentStep.getActionType()) && WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(currentStep.getActionType().substring(0,1))) {
        Map decisionMap = new TreeMap();//simon_del
        for ( int i = 0 ; i < listSize; i++ ) {
          MtmWorkflowProgressUserRecord progressUser = (MtmWorkflowProgressUserRecord) progressUserRecordList.get(i);
          log.debug("MtmWorkflowStepUserActor.getWorkflowAction(): " + progressUser.getOperationParameter() );
          if ( Utility.isEmpty(progressUser.getOperationParameter()) || WorkflowProgress.STATUS_IN_PROGRESS.equals(progressUser.getOperationParameter()) ) {
            // Assume the user is only checked out the task
            continue;
          }
          if ( progressUser.getUserRecordID().intValue() ==0 ) {
            // Automatically completed the task by system
            fullfilledRules = true;
            break outer;
          }
          if ( progressUser.getOperationParameter().length() < 2 || !progressUser.getOperationParameter().startsWith(WorkflowProgress.STATUS_SUBMIT) ) {
            // the workflowAction of a decision point task should be something like S0, S1...
            continue;
          }
          String decision = progressUser.getOperationParameter().substring(1);
          int decisionCount = 1;
          if ( decisionMap.containsKey(decision.trim()) ) {
            decisionCount = ((Integer)decisionMap.get(decision)).intValue()+1 ;
          }
          decisionMap.put ( decision, new Integer(decisionCount) );
          if ( decisionCount >= workflowRule ) {
            log.debug( "decision fullfilled = " + decision );
            fullfilledRules = true;
          }
          //simon_add_S
//          if (WorkflowProgress.STATUS_GENERAL_ACCOMPLISHMENT.equals(progressUser.getWorkflowAction())) {
//            // Automatically completed the task by system
//            fullfilledRules = true;
//            break outer;
//          }
          //simon_add_E
        }
        // End added by Lun
      } else if (WorkflowStep.ACTION_TYPE_TASK_TO_DO.equals(currentStep.getActionType())) {
        int accomplishment = 0;
        for (int i=0; i<listSize; i++) {
          MtmWorkflowProgressUserRecord progressUser = (MtmWorkflowProgressUserRecord) progressUserRecordList.get(i);
          log.debug("MtmWorkflowStepUserActor.getWorkflowAction(): "+progressUser.getWorkflowAction());
          if ( progressUser.getUserRecordID().intValue() ==0 ) {
            fullfilledRules = true;
            break outer;
          } else if (WorkflowProgress.STATUS_GENERAL_ACCOMPLISHMENT.equals(progressUser.getWorkflowAction()) ) {
            accomplishment++;
          }
        }
        log.debug("listSize="+listSize+",workflowRule="+workflowRule+",accomplishment="+accomplishment);
        if (accomplishment >= workflowRule) {
          fullfilledRules = true;
        }
      } else {//WorkflowStep.ACTION_TYPE_SUBMIT
        if (listSize >= workflowRule) {
          fullfilledRules = true;
        }
      }
    }
    
    return fullfilledRules;
  }
  /**
   * Calls after accomplishWorkflowProgressByUser
   *
   * @param currentStep The current step object
   * @param currentProgress The current progress object
   * @param proceedToNextProgress whether to proceed to next task
   * @throws ApplicationException
   */
  protected void createNextWorkflowProgressesAfterCurrentProgress(WorkflowStep currentStep, WorkflowProgress currentProgress, boolean proceedToNextProgress) throws ApplicationException {
    createNextWorkflowProgressesAfterCurrentProgress(currentStep, currentProgress, null, proceedToNextProgress, null);
  }
  /**
   * Calls after accomplishWorkflowProgressByUser, if proceed to next task is needed,
   * also handles the trigger of completion notifications and schedule the next task
   * arrival notifications.
   *
   * @param currentStep The current step
   * @param currentProgress The current progress
   * @param decision Used for user defined task (Decision Point)
   * @param proceedToNextProgress Whether proceed to next progress
   * @param nextStepDueDatesArr Array of next step's due date, if specified by current step
   * @throws ApplicationException
   */
  protected void createNextWorkflowProgressesAfterCurrentProgress(WorkflowStep currentStep, WorkflowProgress currentProgress, String[] decision, boolean proceedToNextProgress, String[] nextStepDueDatesArr) throws ApplicationException {
    boolean rejectedApproval = false;
    boolean isNo = false;
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(sessionCon, dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionCon, dbConn);
    WorkflowOperationManager wfOperationManager = new WorkflowOperationManager(sessionCon, dbConn);
    WorkflowRetrievalManager wfRetrievalManager = new WorkflowRetrievalManager(sessionCon, dbConn);
    CalendarRecordDAObject calendarRecordDAObject = new CalendarRecordDAObject(sessionCon,dbConn);
    WorkflowRecordDAObject workflowRecordDAObject=new WorkflowRecordDAObject(sessionCon,dbConn);
    HolidayManager holidayManager = new HolidayManager(this.sessionCon, this.dbConn);
    
    //*******progressDAO.addActionListener(new WorkflowProgressListener());
    
    // if the workflow step is approval, and the user's choice is reject, set the rejectedApproval flag to 'true'
    if (WorkflowStep.ACTION_TYPE_APPROVAL.equals(currentStep.getActionType()) && WorkflowProgress.STATUS_REJECT.equals(currentProgress.getStatus())) {
      rejectedApproval = true;
    }
    
    // if the workflow step is yes/no, and the user's choice is no, set the isNo flag to 'true'
    if (WorkflowStep.ACTION_TYPE_BOOLEAN.equals(currentStep.getActionType()) && WorkflowProgress.STATUS_NO.equals(currentProgress.getStatus())) {
      isNo = true;
    }
    
    List progressList = progressDAO.getListByTrackID(currentProgress.getTrackID());
    
    log.debug("progressList size: "+progressList.size());
    int seqNoCount = progressList.size() + 1;
    
    if (proceedToNextProgress) {
      wfOperationManager.notifyCompletionByMail(currentStep, currentProgress);
      wfOperationManager.notifyCompletionBySystem(currentStep, currentProgress);
      List childStepList = stepDAO.getListByParentStepID(currentStep.getID());
      log.debug("------currentStep.getID()---------------"+currentStep.getID());
      log.debug("action type: "+currentStep.getActionType());
      String[] parentType = {""};
      if ( WorkflowStep.ACTION_TYPE_APPROVAL.equals(currentStep.getActionType()) ){
        if (rejectedApproval) {
          parentType[0] = WorkflowStep.PARENT_TYPE_REJECTED;
        } else {
          parentType[0] = WorkflowStep.PARENT_TYPE_APPROVED;
        }
      } else if ( WorkflowStep.ACTION_TYPE_BOOLEAN.equals(currentStep.getActionType()) ){
        if (isNo) {
          parentType[0] = WorkflowStep.PARENT_TYPE_NO;
        } else {
          parentType[0] = WorkflowStep.PARENT_TYPE_YES;
        }
      } else if ( WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(currentStep.getActionType()) ) {
        Integer actionID = currentStep.getActionID();
        
        //* get the corresponding workflow action information, and the instance of the class
        WorkflowActionDAObject actionDAO = new WorkflowActionDAObject(sessionCon, dbConn);
        WorkflowAction workflowAction = (WorkflowAction) actionDAO.getObjectByID(actionID);
        WorkflowProcessor processor = AutomaticTaskFactory.getInstance(workflowAction);
        if (processor.getNumOfRoutine() > 1) {
          parentType[0] = WorkflowStep.PARENT_TYPE_SINGLE + this.nextStepRoutineID;
        }
      } else if ( WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(currentStep.getActionType()) ) {
        parentType = decision;
      } else if ( WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(currentStep.getActionType()) ) {
        parentType = decision;
      } else { //*if ( WorkflowStep.ACTION_TYPE_TASK_TO_DO.equals(currentStep.getActionType()) )
        //* Nothing to do in this case at this moment
      }
      
      boolean bClonedAutoExecuteNextStep = this.bAutoExecuteNextStep;
      this.bAutoExecuteNextStep = false;
      int nextStepDueDateIndex = 0;
      for (int i=0; i<childStepList.size(); i++) {
        WorkflowStep childStep = (WorkflowStep) childStepList.get(i);
        List currentStepList=stepDAO.getListCurrentStepByStepID(childStep.getID());//simon_add
        if(currentStepList!=null && currentStepList.size()>0){//simon_add
          
          if(!Utility.isEmpty(parentType)){
          for (int j=0; j<parentType.length; j++) {
            if (checkMergable(currentProgress, childStep, parentType[j])) {
              // add by dick xie;
              //if currentStep at MTM_WF_STEP_WF_STEP the properties LOOP_BACK = "Y" AND LOOP_BACK_ALL_BRANCH="Y";
              //the function is do loop back other pending,in progress ,post progress 
              this.loopbackAllBranch(currentStep, currentProgress, parentType[j]);
              //* see if parentType is matched
              log.debug("pre-create next progress, child workflow step id: "+childStep.getID()+", parent type: "+parentType[j]+", possible chile step parent type: "+childStep.getParentWorkflowStepType());
              if ("".equals(parentType[j]) || parentType[j].equals(childStep.getParentWorkflowStepType()) ) {
                boolean bExecuteFormDrivenStep = false;
                if (WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(childStep.getActionType())) {
                  //                //evalute form equation
                  //                List formEquatList = formEquatDAO.getListByWorkflowStepID(childStep.getID());
                  //                for(int k=0; k<formEquatList.size(); k++) {
                  //                  WorkflowStepFormEquat formEquat = (WorkflowStepFormEquat)formEquatList.get(i);
                  //                  if (!wfOperationManager.compareOperate(formEquat.getOperand(), formEquat.getOperator(), formEquat.getCompare(), TextUtility.parseInteger(formEquat.getValue()), currentProgress.getTrackID()) ) {
                  //                    continue;
                  //                  } else {
                  bExecuteFormDrivenStep = true;
                  //                    break;
                  //                  }
                  //                }
                }
                
                log.debug("is parent type equals empty? "+ ("".equals(parentType[j])) );
                WorkflowProgress newProgress = new WorkflowProgress();
                newProgress.setTrackID(currentProgress.getTrackID());
                newProgress.setWorkflowStepID(childStep.getID());
                newProgress.setSeqNo(new Integer(seqNoCount++));
                newProgress.setCancelFlag(GlobalConstant.FALSE);
                newProgress.setCompleteFlag(GlobalConstant.FALSE);
                newProgress.setStatus(WorkflowProgress.STATUS_PENDING);
                newProgress.setProgressPriority(childStep.getPriority());
                // set the due date according to child step DUE DATE situation.
                
                boolean hasDeuDate=false;
                if  ((!Utility.isEmpty(nextStepDueDatesArr)) && (nextStepDueDateIndex<nextStepDueDatesArr.length)){
                hasDeuDate=Utility.isEmpty(nextStepDueDatesArr[nextStepDueDateIndex]);
              	  
                }
                
                if(WorkflowStep.DYNAMIC_ASSIGN_DUE_DATE_MANDATORY.equals(childStep.getAllowAssignDueDate()) &&
                    (Utility.isEmpty(nextStepDueDatesArr) ) || hasDeuDate  && WorkflowStep.DYNAMIC_ASSIGN_DUE_DATE_MANDATORY.equals(childStep.getAllowAssignDueDate())){
                  throw new ApplicationException(ErrorConstant.WORKFLOW_NEXT_STEP_DUE_DATES_REQUIRED,new ApplicationException(),childStep.getStepName());
                }
                if(!Utility.isEmpty(nextStepDueDatesArr) &&
                    (WorkflowStep.DYNAMIC_ASSIGN_DUE_DATE_TRUE.equals(childStep.getAllowAssignDueDate()) || WorkflowStep.DYNAMIC_ASSIGN_DUE_DATE_MANDATORY.equals(childStep.getAllowAssignDueDate()) ) ){
                  //log.debug("the due date is [" + TextUtility.parseDBDateTimeToTimeStamp( nextStepDueDatesArr[nextStepDueDateIndex]) +"]");
              	  if ((!Utility.isEmpty(nextStepDueDatesArr)) && (nextStepDueDateIndex<nextStepDueDatesArr.length)){
                  newProgress.setDueDate(TextUtility.parseDBDateTimeToTimeStamp( nextStepDueDatesArr[nextStepDueDateIndex]) );
              	  }
                  nextStepDueDateIndex++;
                } else if (!Utility.isEmpty(childStep.getOnNoResponseAfter()) ){
                  newProgress.setDueDate(childStep.getOnNoResponseAfter());
                } else if (!Utility.isEmpty(childStep.getOnNoResponseFor()) ){
                  Timestamp tempDueDate = Utility.addDay(Utility.getCurrentTimestamp(),childStep.getOnNoResponseFor().intValue());
                  Calendar tmpDate = Utility.timestampToCalendar(tempDueDate);                
                  //Utility.setCalendarTime(tmpDate, 23, 59, 59, 0);
                  tempDueDate = Utility.calendarToTimestamp(tmpDate);
                  // Commented by Brook.Huang. Mask working calendar count for [EIP-941].
                  // if (!Utility.isEmpty(childStep.getOnNoResponseCalendarID()) ) {
                  // tempDueDate = holidayManager.getWorkingCalendarDueDateEndDate(
                  // Utility.getCurrentTimestamp(), Utility.dayValue2Millisecond(childStep.getOnNoResponseFor().intValue()), new
                  // Integer(childStep.getOnNoResponseCalendarID()));
                  //                  }                
                  newProgress.setDueDate(tempDueDate );
                }
                
                newProgress.setWorkflowStepSource(childStep); //* set what step information is related to this progress
                
                log.debug("new progress to add: "/*+newProgress*/);
                newProgress = wfOperationManager.createWorkflowProgress(newProgress, bClonedAutoExecuteNextStep);
                newProgress.setWorkflowStepSource(childStep);
                //insert record at che table MTM_WF_PROGRESS_WF_PROGRESS
                this.insertMtmWorkflowProgressMtmWorkflowProgress(currentProgress, newProgress, currentProgress.getChildCreateType());
                
                //* check next step is dynamic assign step's owner, add to list, return to action class
                childStep = (WorkflowStep) wfRetrievalManager.getWorkflowStepInfo(childStep.getID(), null);
                log.debug("workflow step actor type: "+childStep.getAssignActorType());
                if (!bClonedAutoExecuteNextStep && MtmWorkflowStepUserActor.ACTOR_TYPE_RUNTIME_ASSIGN.equals(childStep.getAssignActorType()) ) {
                  assignStepMap.put(childStep.getID(), newProgress);
                } else {
                  // directly add calendar record if not dynamic assign owner
                  Timestamp endDate;
                  if (childStep.getOnNoResponseFor() != null
                      || childStep.getOnNoResponseAfter() != null
                      || ((GlobalConstant.TRUE.equals(childStep.getAllowAssignDueDate()) || WorkflowStep.DYNAMIC_ASSIGN_DUE_DATE_MANDATORY.equals(childStep.getAllowAssignDueDate())) && !Utility.isEmpty(newProgress.getDueDate()) ) ){
                    if ((GlobalConstant.TRUE.equals(childStep.getAllowAssignDueDate()) || WorkflowStep.DYNAMIC_ASSIGN_DUE_DATE_MANDATORY.equals(childStep.getAllowAssignDueDate())) && !Utility.isEmpty(newProgress.getDueDate())){
                      endDate = newProgress.getDueDate();
                    } else if (childStep.getOnNoResponseAfter() != null){
                      endDate = childStep.getOnNoResponseAfter();
                    } else {
                      endDate = Utility.addDay(Utility.getCurrentTimestamp(),childStep.getOnNoResponseFor().intValue());
                      Calendar tmpDate = Utility.timestampToCalendar(endDate);
                      //Utility.setCalendarTime(tmpDate, 23, 59, 59, 0);
                      endDate = Utility.calendarToTimestamp(tmpDate);
                      // Commented by Brook.Huang. Mask working calendar count for [EIP-941].
                      // if (!Utility.isEmpty(childStep.getOnNoResponseCalendarID()) ) {
                      // endDate = holidayManager.getDueDateEndDate(Utility.getCurrentTimestamp(),
                      // childStep.getOnNoResponseFor().intValue()*24*60*60*1000, new Integer(childStep.getOnNoResponseCalendarID()) );
                      //                      }                     
                    }
                    ApplicationContainer applicationContainer = sessionCon.getAppContainer();
                    if (Utility.isEmpty(applicationContainer)) {
                      applicationContainer = new ApplicationContainer();
                      SysFunctionDAObject sysFunctionDAO = new SysFunctionDAObject(null, this.dbConn);
                      applicationContainer.setSysFunctionList(sysFunctionDAO.getFullList());
                    }
                    SysFunction sysFunction = applicationContainer.getSysFunctionByFunctionCode(SystemFunctionConstant.SCHEDULE);
                    if ( sysFunction!=null && "A".equals(sysFunction.getStatus()) ){
                      WorkflowRecord workflowRecord = (WorkflowRecord)workflowRecordDAObject.getObjectByID(childStep.getWorkflowRecordID());
                      //List userList = stepDAO.getUserListByStepID(childStep.getID());
                      List userList = this.getOwnerUserRecordIDListByWorkflowProgressID(newProgress.getID());
                      log.debug("The workflow progress owner list::::::::::::::::"+userList.size());
                      for (int k=0;k<userList.size();k++){
                        CalendarRecord calendarRecord = new CalendarRecord();
                        //calendarRecord.setAlertType("I");
                        log.debug ( "Creating calendar record for user id="+userList.get(k).toString());
                        calendarRecord.setCreatorID((Integer)userList.get(k));
                        calendarRecord.setUpdaterID((Integer)userList.get(k));
                        calendarRecord.setDatetime(endDate);
                        calendarRecord.setEndtime(endDate);
                        calendarRecord.setEventType(CalendarRecord.EVENT_TYPE_CALENDAR_TODO);
                        calendarRecord.setPriority(newProgress.getProgressPriority());
                        calendarRecord.setObjectID(newProgress.getID());
                        calendarRecord.setObjectType(CalendarRecord.OBJECT_TYPE_WORKFLOW);
                        calendarRecord.setReminderType(",");
                        calendarRecord.setTitle(MessageResourcesFactory.getMessage(sessionCon.getSessionLocale(), "calendar.label.pending_workflow_task")+":" +workflowRecord.getWorkflowCode()+ "/" + childStep.getStepSeq() + "."+childStep.getStepName());
                        calendarRecord.setDetail(childStep.getDescription());
                        calendarRecord.setShareType(CalendarRecord.SHARE_TYPE_PRIVATE );
                        calendarRecord.setStatus(GlobalConstant.FALSE);
                        calendarRecord = (CalendarRecord) calendarRecordDAObject.insert(calendarRecord);
                        log.debug("the calendarRecord is objectID and objectType ::::" + calendarRecord.getObjectType() + ":" + calendarRecord.getObjectID().toString());
                      }
                    }
                  }
                }
                
                //* set the assignPriorityStepMap for creating the escalation priority.
                this.assignPriorityStepMap.put(childStep.getID(), newProgress);
                
                log.debug("===bClonedAutoExecuteNextStep:bExecuteFormDrivenStep==="+bClonedAutoExecuteNextStep+":"+bExecuteFormDrivenStep);
                //auto executed
                if (bClonedAutoExecuteNextStep || bExecuteFormDrivenStep) {
                  nextStepRoutineID = 0;
                  numOfRoutine = 1;
                  //bAutoExecuteNextStep = false;
                  MaintWorkflowProgressForm progressForm = new MaintWorkflowProgressForm();
                  newProgress.setStatus(childStep.getActionType().equals(WorkflowStep.ACTION_TYPE_APPROVAL)?WorkflowProgress.STATUS_APPROVE:WorkflowProgress.STATUS_GENERAL_ACCOMPLISHMENT);
                  progressForm.setFormData(newProgress);
                  this.accomplishWorkflowProgressByUser(progressForm);
                }
                log.debug("progress added");
              }
            } else {
              log.debug("**********fail in check mergable***********");
            }
          }
          }
        }//simon_add
      }
    }
  }
  /**
   * Not supported
   *
   * @param currentProgress
   * @param childStep
   * @throws ApplicationException
   */
  public void createNextWorkflowProgressesForSkipWorkflow(WorkflowProgress currentProgress, WorkflowStep childStep) throws ApplicationException {
    
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(sessionCon, dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionCon, dbConn);
    WorkflowOperationManager wfOperationManager = new WorkflowOperationManager(sessionCon, dbConn);
    WorkflowRetrievalManager wfRetrievalManager = new WorkflowRetrievalManager(sessionCon, dbConn);
    CalendarRecordDAObject calendarRecordDAObject = new CalendarRecordDAObject(sessionCon,dbConn);
    WorkflowRecordDAObject workflowRecordDAObject=new WorkflowRecordDAObject(sessionCon,dbConn);
    HolidayManager holidayManager = new HolidayManager(this.sessionCon, this.dbConn);
    
    //*******progressDAO.addActionListener(new WorkflowProgressListener());
    // get parent Step info for never use
//    List parentStepList = stepDAO.getListByChildStepID(childStep.getID());
//    WorkflowStep currentStep = null;
//    if (!Utility.isEmpty(parentStepList)) {
//      currentStep = (WorkflowStep)parentStepList.get(0);
//    }
    
    List progressList = progressDAO.getListByTrackID(currentProgress.getTrackID());
    
    log.debug("progressList size: "+progressList.size());
    int seqNoCount = progressList.size() + 1;
    
    
    
    
    boolean bClonedAutoExecuteNextStep = this.bAutoExecuteNextStep;
    this.bAutoExecuteNextStep = false;
    
    boolean bExecuteFormDrivenStep = false;
    if (WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(childStep.getActionType())) {
      //       //evalute form equation
      //List formEquatList = formEquatDAO.getListByWorkflowStepID(childStep.getID());
      //       for(int k=0; k<formEquatList.size(); k++) {
      //       WorkflowStepFormEquat formEquat = (WorkflowStepFormEquat)formEquatList.get(i);
      //       if (!wfOperationManager.compareOperate(formEquat.getOperand(), formEquat.getOperator(), formEquat.getCompare(), TextUtility.parseInteger(formEquat.getValue()), currentProgress.getTrackID()) ) {
      //       continue;
      //       } else {
      bExecuteFormDrivenStep = true;
      //        break;
      //        }
      //      }
    }
    
    WorkflowProgress newProgress = new WorkflowProgress();
    newProgress.setTrackID(currentProgress.getTrackID());
    newProgress.setWorkflowStepID(childStep.getID());
    newProgress.setSeqNo(new Integer(seqNoCount++));
    newProgress.setCancelFlag(GlobalConstant.FALSE);
    newProgress.setCompleteFlag(GlobalConstant.FALSE);
    newProgress.setStatus(WorkflowProgress.STATUS_PENDING);
    
    // set the due date according to child step DUE DATE situation.
    if (!Utility.isEmpty(childStep.getOnNoResponseAfter()) ){
      newProgress.setDueDate(childStep.getOnNoResponseAfter());
    } else if (!Utility.isEmpty(childStep.getOnNoResponseFor()) ){
      Timestamp tempDueDate = Utility.addDay(Utility.getCurrentTimestamp(),childStep.getOnNoResponseFor().intValue());
      Calendar tmpDate = Utility.timestampToCalendar(tempDueDate);
      //Utility.setCalendarTime(tmpDate, 23, 59, 59, 0);
      tempDueDate = Utility.calendarToTimestamp(tmpDate);
      // Commented by Brook.Huang. Mask working calendar count for [EIP-941].
      // if (!Utility.isEmpty(childStep.getOnNoResponseCalendarID()) ) {
      // tempDueDate = holidayManager.getDueDateEndDate(Utility.getCurrentTimestamp(), childStep.getOnNoResponseFor().intValue()*24*60*60*1000, new
      // Integer(childStep.getOnNoResponseCalendarID()) );
      //      }  
      newProgress.setDueDate(tempDueDate );
    }
    
    newProgress.setWorkflowStepSource(childStep); //* set what step information is related to this progress
    
    log.debug("new progress to add: "/*+newProgress*/);
    newProgress =  wfOperationManager.createWorkflowProgress(newProgress, bClonedAutoExecuteNextStep);
    newProgress.setWorkflowStepSource(childStep);
    //insert record at che table MTM_WF_PROGRESS_WF_PROGRESS
    this.insertMtmWorkflowProgressMtmWorkflowProgress(currentProgress, newProgress, currentProgress.getChildCreateType());
    
    Timestamp endDate;
    
    if (childStep.getOnNoResponseFor() != null
        || childStep.getOnNoResponseAfter() != null
        || ((GlobalConstant.TRUE.equals(childStep.getAllowAssignDueDate()) || WorkflowStep.DYNAMIC_ASSIGN_DUE_DATE_MANDATORY.equals(childStep.getAllowAssignDueDate())) && !Utility.isEmpty(newProgress.getDueDate()) ) ){
      if ((GlobalConstant.TRUE.equals(childStep.getAllowAssignDueDate()) || WorkflowStep.DYNAMIC_ASSIGN_DUE_DATE_MANDATORY.equals(childStep.getAllowAssignDueDate())) && !Utility.isEmpty(newProgress.getDueDate())){
        endDate = newProgress.getDueDate();
      } else if (childStep.getOnNoResponseAfter() != null){
        endDate = childStep.getOnNoResponseAfter();
      } else {
        endDate = Utility.addDay(Utility.getCurrentTimestamp(),childStep.getOnNoResponseFor().intValue());
        Calendar tmpDate = Utility.timestampToCalendar(endDate);
        endDate = Utility.calendarToTimestamp(tmpDate);
        // Commented by Brook.Huang. Mask working calendar count for [EIP-941].
        // if (!Utility.isEmpty(childStep.getOnNoResponseCalendarID()) ) {
        // endDate = holidayManager.getDueDateEndDate(Utility.getCurrentTimestamp(), childStep.getOnNoResponseFor().intValue()*24*60*60*1000, new
        // Integer(childStep.getOnNoResponseCalendarID()) );
        //        }  
      }
      ApplicationContainer applicationContainer = sessionCon.getAppContainer();
      SysFunction sysFunction = applicationContainer.getSysFunctionByFunctionCode(SystemFunctionConstant.SCHEDULE);
      if ( sysFunction!=null && "A".equals(sysFunction.getStatus()) ){
        WorkflowRecord workflowRecord = (WorkflowRecord)workflowRecordDAObject.getObjectByID(childStep.getWorkflowRecordID());
        List userList = stepDAO.getUserListByStepID(childStep.getID());
        for (int k=0;k<userList.size();k++){
          CalendarRecord calendarRecord = new CalendarRecord();
          //calendarRecord.setAlertType("I");
          calendarRecord.setCreatorID((Integer)userList.get(k));
          calendarRecord.setUpdaterID((Integer)userList.get(k));
          calendarRecord.setDatetime(endDate);
          calendarRecord.setEventType(CalendarRecord.EVENT_TYPE_CALENDAR_TODO);
          calendarRecord.setObjectID(newProgress.getID());
          calendarRecord.setObjectType(CalendarRecord.OBJECT_TYPE_WORKFLOW);
          calendarRecord.setPriority(newProgress.getProgressPriority());
          calendarRecord.setReminderType(",");
          calendarRecord.setTitle(MessageResourcesFactory.getMessage(sessionCon.getSessionLocale(), "calendar.label.pending_workflow_task")+":" +workflowRecord.getWorkflowCode()+ "/" + childStep.getStepSeq() + "."+childStep.getStepName());
          calendarRecord.setDetail(childStep.getDescription());
          calendarRecord.setShareType(CalendarRecord.SHARE_TYPE_PRIVATE );
          calendarRecord.setStatus(GlobalConstant.FALSE);
          calendarRecordDAObject.insert(calendarRecord);
        }
      }
    }
    //* check next step is dynamic assign step's owner, add to list, return to action class
    childStep = (WorkflowStep) wfRetrievalManager.getWorkflowStepInfo(childStep.getID(), null);
    log.debug("workflow step actor type: "+childStep.getActorType());
    if (!bClonedAutoExecuteNextStep && MtmWorkflowStepUserActor.ACTOR_TYPE_RUNTIME_ASSIGN.equals(childStep.getActorType()) ) {
      assignStepMap.put(childStep.getID(), newProgress);
    }
    //* set the assignPriorityStepMap for creating the escalation priority.
    this.assignPriorityStepMap.put(childStep.getID(), newProgress);
    
    log.debug("===bClonedAutoExecuteNextStep:bExecuteFormDrivenStep==="+bClonedAutoExecuteNextStep+":"+bExecuteFormDrivenStep);
    if (bClonedAutoExecuteNextStep || bExecuteFormDrivenStep) {
      nextStepRoutineID = 0;
      numOfRoutine = 1;
      
      //bAutoExecuteNextStep = false;
      
      MaintWorkflowProgressForm progressForm = new MaintWorkflowProgressForm();
      newProgress.setStatus(childStep.getActionType().equals(WorkflowStep.ACTION_TYPE_APPROVAL)?WorkflowProgress.STATUS_APPROVE:WorkflowProgress.STATUS_GENERAL_ACCOMPLISHMENT);
      progressForm.setFormData(newProgress);
      this.accomplishWorkflowProgressByUser(progressForm);
      
    }
    
  }
  
  /**
   * Checks if the merge step operation succeed.
   *
   * @param currentProgress The current progress
   * @param nextStep The next step to be merged
   * @return true if the check succeeded
   * @throws ApplicationException
   */
  public boolean checkMergable(WorkflowProgress currentProgress, WorkflowStep nextStep, String parentType) throws ApplicationException {
    boolean result = true;
    
    MtmWorkflowStepWorkflowStepDAObject mtmWkWkDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionCon, dbConn);
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(sessionCon, dbConn);
    
    log.debug("parent step id: "+currentProgress.getWorkflowStepID()+", child step id: "+nextStep.getID()+", parentType="+parentType);
    String checkParentType = parentType;
    if(WorkflowProgress.STATUS_GENERAL_ACCOMPLISHMENT.equals(parentType)){
      checkParentType = WorkflowStep.PARENT_TYPE_SINGLE;
    }
    List currentGroupStepList = mtmWkWkDAO.getListByParentIDChildIDAndParentType(currentProgress.getWorkflowStepID(), nextStep.getID(), checkParentType);
    log.debug("currentGroupStepList size: "+currentGroupStepList.size());
    if (currentGroupStepList.size() > 0) {
      MtmWorkflowStepWorkflowStep currentMtmWkWk = (MtmWorkflowStepWorkflowStep) currentGroupStepList.get(0); // should only have one record for the given parent step id and child step id
      log.debug("workflow record id: "+nextStep.getWorkflowRecordID()+", workflow group id: "+currentMtmWkWk.getGroupID());
      List currentGroupList = mtmWkWkDAO.getListUnderSameGroupByWorkflowRecordIDAndGroupID(nextStep.getWorkflowRecordID(), currentMtmWkWk.getGroupID());
      log.debug("currentGroupList size: "+currentGroupList.size());
      if(currentGroupList.size()==1){
        log.debug("Only one step in the list, no step to merge with");
        return true;
      }
      //* great a list of step id that need to check for mergable
      List mergableStepIDList = new ArrayList();
      for (int i=0; i<currentGroupList.size(); i++) {
        MtmWorkflowStepWorkflowStep mtmWkWk = (MtmWorkflowStepWorkflowStep) currentGroupList.get(i);
        mergableStepIDList.add(mtmWkWk.getParentStepID());
        log.debug("the petParentStep id :   " + mtmWkWk.getParentStepID());
      }
      log.debug("mergableStepIDList: "+mergableStepIDList);
      log.debug("track id: "+currentProgress.getTrackID()+", current progress status: "+currentProgress.getStatus());
      // get the list of progress with this track and step id list
      //      List stepExecutedInProgress = progressDAO.getListByTrackIDStepIDListAndStatus(currentProgress.getTrackID(), mergableStepIDList, currentProgress.getStatus());
      List stepExecutedInProgress = progressDAO.getListByTrackIDStepIDListAndStatus(currentProgress.getTrackID(), mergableStepIDList, null);
      log.debug("stepExecutedInProgress size: "+stepExecutedInProgress.size());
      
      // get the list of progress by the currentStepID which is about to pre-add for max check
      List progressListForMaxCheck = progressDAO.getListByStepIDAndTrackID(nextStep.getID(), currentProgress.getTrackID());
      log.debug("progressListForMaxCheck size: "+progressListForMaxCheck.size());
      
      if (progressListForMaxCheck.size() == 0) {
        int matchedCount = 0;
        
        //* check how many has done
        for(int i=0;i<currentGroupList.size();i++){
          MtmWorkflowStepWorkflowStep mtmWkWk = (MtmWorkflowStepWorkflowStep) currentGroupList.get(i);
          List progressList = progressDAO.getListByStepIDAndTrackID(mtmWkWk.getParentStepID(), currentProgress.getTrackID());
          for(int j=0;j<progressList.size();j++){
            WorkflowProgress progress = (WorkflowProgress)progressList.get(j);
            if(WorkflowProgress.STATUS_SUBMIT.equals(progress.getStatus()) ||
                WorkflowProgress.STATUS_GENERAL_ACCOMPLISHMENT.equals(progress.getStatus()) ||
                WorkflowProgress.STATUS_APPROVE.equals(progress.getStatus()) ||
                WorkflowProgress.STATUS_REJECT.equals(progress.getStatus()) ||
                WorkflowProgress.STATUS_YES.equals(progress.getStatus()) ||
                WorkflowProgress.STATUS_NO.equals(progress.getStatus()) )
            {
              log.debug("checking progress, ID="+progress.getID()+", status="+progress.getStatus()+", parentType="+mtmWkWk.getParentType());
              if( WorkflowStep.PARENT_TYPE_SINGLE.equals(mtmWkWk.getParentType()) && (WorkflowProgress.STATUS_SUBMIT.equals(progress.getStatus())||WorkflowProgress.STATUS_GENERAL_ACCOMPLISHMENT.equals(progress.getStatus())) ){
                matchedCount++;
              }else if( WorkflowStep.PARENT_TYPE_APPROVED.equals(mtmWkWk.getParentType()) ||
                  WorkflowStep.PARENT_TYPE_REJECTED.equals(mtmWkWk.getParentType()) ||
                  WorkflowStep.PARENT_TYPE_YES.equals(mtmWkWk.getParentType()) ||
                  WorkflowStep.PARENT_TYPE_NO.equals(mtmWkWk.getParentType())
              )
              {
                if(mtmWkWk.getParentType().equals(progress.getStatus())){
                  matchedCount++;
                }
                //Handle the decision point and form-value driven task case
              } else if (mtmWkWk.getParentType().startsWith("S") && mtmWkWk.getParentType().length() > 1 ) {
                if (WorkflowProgress.STATUS_GENERAL_ACCOMPLISHMENT.equals(progress.getStatus())) {
                  // check if the user really selected this decision
                  if (currentProgress.getWorkflowStepID().equals(progress.getWorkflowStepID()) && mtmWkWk.getParentType().equals(checkParentType)){
                    matchedCount++;
                  }else if (!currentProgress.getWorkflowStepID().equals(progress.getWorkflowStepID())){
                    MtmWorkflowProgressUserRecordDAObject mtmProgressUserRecordDAO = new MtmWorkflowProgressUserRecordDAObject(sessionCon, dbConn);
                    Map mostWantedRoute = mtmProgressUserRecordDAO.getMostWantedRouteByProgressID ( progress.getID());
                    if(mostWantedRoute.containsKey(mtmWkWk.getParentType())){
                      matchedCount++;
                    }
                  }
                }
              }
            }
          }  // End for(progressList)
        }
        log.debug("matched count: "+matchedCount);
        if (matchedCount < currentGroupList.size()) {
          result = false;
        }
      } else {
        //this part of flow has visited before, and is revisited now (may due to rejection or routing. needed to filter those old data first
        List actualCompletedStepAfterFilter = new ArrayList();
        
        // getting the last one implicitly assuming that is it the one with max. seq no
        WorkflowProgress lastWorkflowProgress = (WorkflowProgress) progressListForMaxCheck.get(progressListForMaxCheck.size() - 1);
        log.debug("lastWorkflowProgress: "+lastWorkflowProgress.getID());
        for (int i=0; i<stepExecutedInProgress.size(); i++) {
          WorkflowProgress progressInTrack = (WorkflowProgress) stepExecutedInProgress.get(i);
          
          //if the progress's seq no larger than lastWorkflowProgress's seq no means it is a valid progress to count for a successful case of merge
          if (progressInTrack.getSeqNo().intValue() > lastWorkflowProgress.getSeqNo().intValue()) {
            actualCompletedStepAfterFilter.add(progressInTrack);
          }
        }
        
        log.debug("actualCompletedStepAfterFilter.size(): "+actualCompletedStepAfterFilter.size()+", currentGroupList.size(): "+currentGroupList.size());
        if (actualCompletedStepAfterFilter.size() == currentGroupList.size()) {
          int matchedCount = 0;
          //* check how many has done
          for(int j=0;j<actualCompletedStepAfterFilter.size();j++){
            WorkflowProgress progress = (WorkflowProgress)actualCompletedStepAfterFilter.get(j);
            log.debug("########################## Progress Status : "+progress.getStatus()+"  Progress ID :"+progress.getID());
            if(WorkflowProgress.STATUS_SUBMIT.equals(progress.getStatus()) ||
                WorkflowProgress.STATUS_GENERAL_ACCOMPLISHMENT.equals(progress.getStatus()) ||
                WorkflowProgress.STATUS_APPROVE.equals(progress.getStatus()) ||
                WorkflowProgress.STATUS_REJECT.equals(progress.getStatus()) ||
                WorkflowProgress.STATUS_YES.equals(progress.getStatus()) ||
                WorkflowProgress.STATUS_NO.equals(progress.getStatus()) ){
              
              for(int k=0; k<currentGroupList.size(); k++){
                MtmWorkflowStepWorkflowStep mtmWkWk = (MtmWorkflowStepWorkflowStep) currentGroupList.get(k);
                if(mtmWkWk.getParentStepID().equals(progress.getWorkflowStepID()) ) {
                  if( WorkflowStep.PARENT_TYPE_SINGLE.equals(mtmWkWk.getParentType()) && (WorkflowProgress.STATUS_SUBMIT.equals(progress.getStatus())||WorkflowProgress.STATUS_GENERAL_ACCOMPLISHMENT.equals(progress.getStatus())) ){
                    matchedCount++;
                  }else if( WorkflowStep.PARENT_TYPE_APPROVED.equals(mtmWkWk.getParentType()) ||
                      WorkflowStep.PARENT_TYPE_REJECTED.equals(mtmWkWk.getParentType()) ||
                      WorkflowStep.PARENT_TYPE_YES.equals(mtmWkWk.getParentType()) ||
                      WorkflowStep.PARENT_TYPE_NO.equals(mtmWkWk.getParentType())
                  ) {
                    if(mtmWkWk.getParentType().equals(progress.getStatus())){
                      matchedCount++;
                    }
                    //Handle the decision point and form-value driven task case
                  } else if (mtmWkWk.getParentType().startsWith("S") && mtmWkWk.getParentType().length() > 1 ) {
                    if (WorkflowProgress.STATUS_GENERAL_ACCOMPLISHMENT.equals(progress.getStatus())) {
                      // check if the user really selected this decision
                      if (currentProgress.getWorkflowStepID().equals(progress.getWorkflowStepID()) && mtmWkWk.getParentType().equals(checkParentType)){
                        matchedCount++;
                      }else if (!currentProgress.getWorkflowStepID().equals(progress.getWorkflowStepID())){
                        MtmWorkflowProgressUserRecordDAObject mtmProgressUserRecordDAO = new MtmWorkflowProgressUserRecordDAObject(sessionCon, dbConn);
                        Map mostWantedRoute = mtmProgressUserRecordDAO.getMostWantedRouteByProgressID ( progress.getID());
                        if(mostWantedRoute.containsKey(mtmWkWk.getParentType())){
                          matchedCount++;
                        }
                      }
                    }
                  }
                }
              }
            }
            
          }
          
          log.debug("==================matched count: "+matchedCount);
          if (matchedCount < currentGroupList.size()) {
            result = false;
          }
        } else {
          // if not equal means some step even not yet arrive to the merging group
          result = false;
        }
      }  // End if (progressListForMaxCheck.size() == 0)
    }else{
      // This parent/child step is not belongs to the specified parentType
      return false;
    }  // End if (currentGroupStepList.size() > 0)
    log.debug("######## result ##############:"+result);
    return result;
  }
  
  /**
   * Starts a workflow track by the given form data, automatically finished the first progress
   *
   * @param form The AbstractActionForm containing the starting workflow data
   * @return The WorkflowProgress object after started the workflow
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.WorkflowRecord
   */
  public AbstractBaseObject startWorkflowTrack(AbstractActionForm form,Integer autoTaskOwnerID) throws ApplicationException {
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionCon, dbConn);
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(sessionCon, dbConn);
    MtmWorkflowProgressSystemObjectDAObject progressObjectDAO = new MtmWorkflowProgressSystemObjectDAObject(sessionCon, dbConn);
    DmsDocumentDAObject dmsDocDAO = new DmsDocumentDAObject(sessionCon, dbConn);
    FormSubmissionDAObject formSubmissionDAO = new FormSubmissionDAObject(sessionCon, dbConn);
    MtmWfStepFormRecordDAObject stepFormRecordDAO = new MtmWfStepFormRecordDAObject(sessionCon, dbConn);
    WorkflowProgressActionTakerCheckerManager progressCheckManager = new WorkflowProgressActionTakerCheckerManager(sessionCon, dbConn);
    
    MaintWorkflowRecordForm recordForm = (MaintWorkflowRecordForm) form;
    WorkflowRecord record = (WorkflowRecord) recordForm.getFormData();
    WorkflowStep step = (WorkflowStep) stepDAO.getSubmitStepByWorkflowRecordID(record.getID());
    
    //CHECK SOME THING BEFORE SUBMIT THE RECORD
    //if not pass check method will throw a exception
    progressCheckManager.checkIfThisUserCanSubmitThisWorkflow(record.getID());
      
    //CHECK END
    if (step!=null) {
      Integer trackID = null;
      if( Utility.isEmpty(recordForm.getTrackID()) ){
        trackID = DataSourceFactory.getNextSequence("WORKFLOW_TRACK_ID");
      } else {
        trackID = new Integer(recordForm.getTrackID()); 
      }  
      
      WorkflowProgress progress = null;
      
      progress = (WorkflowProgress)progressDAO.getFirstProgressByTrackID(trackID);
      if(Utility.isEmpty(progress)){
        progress = new WorkflowProgress();
        progress.setWorkflowStepID(step.getID());
        progress.setTrackID(trackID);
        progress.setSeqNo(new Integer(1));
        progress.setCancelFlag(GlobalConstant.FALSE);
        progress.setCompleteFlag(GlobalConstant.FALSE);
        progress.setStatus(WorkflowProgress.STATUS_SUBMIT);
        progress.setProgressPriority(step.getPriority());
        progress = (WorkflowProgress) progressDAO.insertObject(progress);
      }          	
      
      //record Mtm_WF_Progress_Access_Form when submit the form
      if( recordForm.getWorkflowObject() instanceof FormSubmission){
        this.recordSubmitForm(progress.getID(), recordForm);
      }
      
      //* prepare a workflow progress form for accomplish of the submit step
      MaintWorkflowProgressForm progressForm = new MaintWorkflowProgressForm();
      progressForm.setFormData(progress);
      progressForm.setReason(recordForm.getComment());
      
      //* set the next step due dates
      progressForm.setNextStepDueDates(recordForm.getNextStepDueDates());
      
      // set the AttachmentList
      if ( !Utility.isEmpty(recordForm.getFileNames()) && !"null".equals(recordForm.getFileNames()) ) {
        com.dcivision.upload.core.MultipleFileUploadUtil uploadUtil = new com.dcivision.upload.core.MultipleFileUploadUtil(sessionCon);
        List attachmentList = uploadUtil.getWorkflowAttachmentList(recordForm.getFileNames(), WorkflowProgressAttachment.ATTACHED_SINGLE_FILE);
        progressForm.setAttachmentList(attachmentList);
      }
 
      // set the AttachmentList from ParaDOC
      if (!Utility.isEmpty(recordForm.getDmsFileNames()) && !"null".equals(recordForm.getFileNames()) ){
        List dmsAttachmentList = this.getDmsDocumentAttachment(recordForm.getDmsFileNames());
        List attachmentList = new ArrayList();
        if (progressForm.getAttachmentList()!=null) {
          attachmentList = progressForm.getAttachmentList();
        }
        if(!Utility.isEmpty(dmsAttachmentList)){
          attachmentList.addAll(dmsAttachmentList);
        }
        progressForm.setAttachmentList(attachmentList);
      }
      
      //handle the ParaDOC Paper Document workflow
      if(form instanceof com.dcivision.workflow.web.MaintWorkflowRecordForm){
        MaintWorkflowRecordForm maintForm = (MaintWorkflowRecordForm)form;
        if(!Utility.isEmpty(maintForm.getDocumentID())){
          recordForm.setWorkflowObject(dmsDocDAO.getObjectByID(new Integer(maintForm.getDocumentID())) );
        }
      }
      //* add workflow progress system object record, if any
      if (recordForm.getWorkflowObject() != null) {       
        if(recordForm.getWorkflowObject() instanceof com.dcivision.form.bean.FormSubmission ){
          
          List pendingSavedList = formSubmissionDAO.getPendingSavedListByTrackID(trackID);
          
          //check if the form bind with the workflow record at the first step.          
          for (int  j = 0; j < pendingSavedList.size(); j++) {
            FormSubmission formSubmission = (FormSubmission)pendingSavedList.get(j);
            List bindObjectList = stepFormRecordDAO.getListByWorkflowRecordIDFormRecordID(formSubmission.getWorkflowRecordID(), formSubmission.getFormRecordID());
            
            if (Utility.isEmpty(bindObjectList)) {
              //change the Form Submission Status
            	formSubmission.setStatus(FormSubmission.FORM_SUBMISSION_STATU_SUBMIT);
            	formSubmissionDAO.updateObject(formSubmission);
              
              //record the MTM_Workflow_Progress_System_Object Table              
              JobDataMap dataMap = new JobDataMap();
              dataMap.put("WORKFLOW_OBJECT", formSubmission);
              dataMap.put("WORKFLOW_PROGRESS", progress);
              dataMap.put("OBJECT_PARAMETER", recordForm.getObjParamMap());
              
              MtmWorkflowProgressSystemObject systemObject = new MtmWorkflowProgressSystemObject();
              systemObject.setBeanClassName(recordForm.getWorkflowObject().getClass().getName());
              systemObject.setObjectID(formSubmission.getID());
              systemObject.setTrackID(trackID);
              systemObject.setWorkflowProgressID(progress.getID());
              systemObject.setParameterObject(dataMap.getWrappedMap());
              progressObjectDAO.insertObject(systemObject);
              
              //remove the submission from the Pending or Save List
              pendingSavedList.remove(formSubmission);
            }
          }
          
          //check if exist other save form submission
          if(Utility.isEmpty(pendingSavedList)){
            FormSubmission tmpSubmission = (FormSubmission)recordForm.getWorkflowObject();
            if(FormSubmission.FORM_SUBMISSION_STATU_SAVED.equals(tmpSubmission.getStatus())){
              progressForm.setStatus(WorkflowProgress.STATUS_IN_PROGRESS);
            }else{
              progressForm.setStatus(WorkflowProgress.STATUS_SUBMIT);
            }
          } else {
            progressForm.setStatus(WorkflowProgress.STATUS_IN_PROGRESS);
          }
          
        } 
        
        Object object = progressObjectDAO.getObjectByTrackIDWorkflowProgressIDObjectID(
            trackID, progress.getID(), recordForm.getWorkflowObject().getID());
        
        if (Utility.isEmpty(object)) {
          JobDataMap dataMap = new JobDataMap();
          dataMap.put("WORKFLOW_OBJECT", recordForm.getWorkflowObject());
          dataMap.put("WORKFLOW_PROGRESS", progress);
          dataMap.put("OBJECT_PARAMETER", recordForm.getObjParamMap());
          
          MtmWorkflowProgressSystemObject systemObject = new MtmWorkflowProgressSystemObject();
          systemObject.setBeanClassName(recordForm.getWorkflowObject().getClass().getName());
          systemObject.setObjectID(recordForm.getWorkflowObject().getID());
          systemObject.setTrackID(trackID);
          systemObject.setWorkflowProgressID(progress.getID());
          systemObject.setParameterObject(dataMap.getWrappedMap());
          progressObjectDAO.insertObject(systemObject);
        }
        
      }
      
      log.debug("created the first progress, now have to automatically finish it");
      this.accomplishWorkflowProgressByUser(progressForm,autoTaskOwnerID);
      
      return progress;
    } else {
      return null;
    }
  }
  
  public AbstractBaseObject startWorkflowTrack(AbstractActionForm form) throws ApplicationException {
    return startWorkflowTrack(form,null);
  }
 
  
  // -----------------------------
  // -- Automatic Task Handling --
  // -----------------------------
  /**
   * Executes an automatic task
   *
   * @param wstep The task step object
   * @param wprogress The task progress object
   * @throws ApplicationException
   * @see com.dcivision.workflow.core.WorkflowProcessor
   */
  public synchronized void executeAutomaticTask(WorkflowStep wstep, WorkflowProgress wprogress) throws ApplicationException {
    if (WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(wstep.getActionType())) {
      try {
        log.debug("Start Automatic Task: Track ID: "+wprogress.getTrackID()+", Progress ID: "+wprogress.getID());
        WorkflowActionDAObject workflowActionDAO = new WorkflowActionDAObject(sessionCon, dbConn);
        WorkflowAction workflowAction = (WorkflowAction)workflowActionDAO.getObjectByID(wstep.getActionID());
        WorkflowProcessor processor = AutomaticTaskFactory.getInstance(workflowAction);

        /**
         * assign connection into the processor
         */
        processor.setConnection(dbConn);
        
        MtmWorkflowProgressSystemObjectDAObject mtmWorkflowProgressSystemObjectDAO = new MtmWorkflowProgressSystemObjectDAObject(sessionCon, dbConn);
        /**
         * @TODO To get the MtmWorkflowProgressSystemObject by trackID & objectID
         */
        MtmWorkflowProgressSystemObject progressObj = null;
        List objectList = mtmWorkflowProgressSystemObjectDAO.getListByTrackID(wprogress.getTrackID());
        if(!Utility.isEmpty(objectList)){
          progressObj = (MtmWorkflowProgressSystemObject)objectList.get(0);  
        }
        //MtmWorkflowProgressSystemObject progressObj = (MtmWorkflowProgressSystemObject)mtmWorkflowProgressSystemObjectDAO.getObjectByTrackID(wprogress.getTrackID());
        
        JobDataMap dataMap = null;
        if (progressObj!=null) {
          dataMap = new JobDataMap((Map) progressObj.getParameterObject());
        }
        
        processor.execute(dataMap, wprogress);
        this.numOfRoutine = processor.getNumOfRoutine();
        this.nextStepRoutineID = processor.getResultRoutine();
        this.bAutoExecuteNextStep = processor.isAutoExecuteNextStep();
        this.bCreateNextWorkflowProgress = processor.isCreateNextWorkflowProgress();
        this.currentProgressStatus= processor.getCurrentProgressStatus();
        log.debug("End Automatic Task");
      } catch (ApplicationException appEx) {
        throw appEx;
      } catch (Exception e) {
        log.error("Error when executing automatic task.", e);
        throw new ApplicationException(e);
      }
    }
  }
  
  /**
   * Generates Dms Document Attachment list by submitted form parameter string.
   * The String is in format [versionID]:fileName:[fileSize] which looks like:
   * <pre>
   *   [999]:File 1:[55];[998]:File 2:[33];
   * </pre>
   *
   * @param dmsFileNames A semi-colon separated string of attachment name
   * @return List of WorkflowProgressAttachment
   * @see com.dcivision.workflow.bean.WorkflowProgressAttachment
   */
  private List getDmsDocumentAttachment(String dmsFileNames){
    
    List resultList = new ArrayList();
    WorkflowProgressAttachment attachment = null;
    
    String[] fileNameArray = TextUtility.splitString(dmsFileNames,GlobalConstant.FILENAME_SEPARATOR);
    
    for (int i=0; i<fileNameArray.length; i++) {
      String[] arrName =  TextUtility.splitString(fileNameArray[i],":");
      
      String externalID = arrName[0];
      String fileName = arrName[1];
      String contentSize = arrName[2];
      externalID = externalID.substring(1,externalID.length()-1);
      fileName = fileName.substring(1,fileName.length()-1);
      contentSize = contentSize.substring(1,contentSize.length()-1);
      
      attachment = new WorkflowProgressAttachment();
      attachment.setAttachmentName(fileName);
      attachment.setExternalID(externalID);
      attachment.setFlowType(WorkflowProgressAttachment.ATTACHED_SINGLE_FILE);
      //attachment.setDataStream(inputStream);
      attachment.setContentSize(new Integer(contentSize));
      attachment.setAttachmentType(WorkflowProgressAttachment.ATTACHED_DMS_VERSION_SYSTEM_ID);
      resultList.add(attachment);
    }
    
    return resultList;
    
  }
  
  
  /**
   * Gets the last completed progress action taker by workflow step ID & track ID
   *
   * @param workflowStepID The step ID
   * @param trackID The track ID
   * @return List of Integer storing the user record ID
   * @throws ApplicationException
   */
  public  List  getLastCompletedProgressUserRecordID(Integer workflowStepID,Integer trackID) throws ApplicationException{
    WorkflowProgressDAObject workflowProgressDAObject = new WorkflowProgressDAObject(sessionCon, dbConn);
    Integer progressID = null;
    
    List wfProgressUserRecordList = new ArrayList();
    List userRecordIDList = new ArrayList();
    MtmWorkflowProgressUserRecordDAObject mtmWorkflowProgressUserRecordDAObject = new MtmWorkflowProgressUserRecordDAObject(sessionCon, dbConn);
    
    //get the last progress ID for this step
    progressID = workflowProgressDAObject.getLastProgressIDByTrackIDAndStepID(trackID,workflowStepID);
    //get the actor taker for this step
    wfProgressUserRecordList = mtmWorkflowProgressUserRecordDAObject.getCompletedUserProcessRecordByWorkflowProgressID(progressID);
    for(int i = 0;i < wfProgressUserRecordList.size(); i++){
      Integer userRecordID = null;
      MtmWorkflowProgressUserRecord mtmWorkflowProgressUserRecord = new MtmWorkflowProgressUserRecord();
      mtmWorkflowProgressUserRecord = (MtmWorkflowProgressUserRecord)wfProgressUserRecordList.get(i);
      userRecordID = mtmWorkflowProgressUserRecord.getUserRecordID();
      userRecordIDList.add(userRecordID);
    }
    
    return userRecordIDList;
  }
  
  /**
   * Gets the last completed progress action taker's Report To by workflow step ID & track ID
   *
   * @param workflowStepID The step ID
   * @param trackID The track ID
   * @return List of Integer storing the user record ID
   * @throws ApplicationException
   */
  public  List  getLastCompletedProgressUserRecordReportToID(Integer workflowStepID,Integer trackID) throws ApplicationException{
    UserRecordDAObject userRecordDAO = new UserRecordDAObject(sessionCon, dbConn);
    List reportToList = new ArrayList();
    List userRecordIDList = new ArrayList();
    userRecordIDList = this.getLastCompletedProgressUserRecordID(workflowStepID, trackID);
    if(!Utility.isEmpty(userRecordIDList)){
      for (int i=0; i<userRecordIDList.size(); i++){
        AbstractBaseObject userRecord = userRecordDAO.getReportToUserByID( (Integer)userRecordIDList.get(i) );
        if ( !Utility.isEmpty(userRecord) ) {
          reportToList.add(userRecord.getID());
        }
      }
    }
    return reportToList;
  }
  
  /**
   * Gets a List of possible owners by a given workflow progress ID
   *
   * @param workflowProgressID The workflow progress ID
   * @return List of Integer storing the user record ID
   * @throws ApplicationException
   */
  public List getOwnerUserRecordIDListByWorkflowProgressID(Integer workflowProgressID) throws ApplicationException {
    List actorUserIDList = new ArrayList();
    
    UserRecordDAObject userDAO = new UserRecordDAObject(this.sessionCon, this.dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(this.sessionCon, this.dbConn);
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(this.sessionCon, this.dbConn);
    MtmWorkflowStepUserActorDAObject userActorDAO = new MtmWorkflowStepUserActorDAObject(this.sessionCon, this.dbConn);
    MtmWorkflowProgressUserRecordDAObject progressUserRecordDAO = new MtmWorkflowProgressUserRecordDAObject(this.sessionCon, this.dbConn);
    MtmWfProgressWfProgressDAObject wfProgressTowDAO = new MtmWfProgressWfProgressDAObject(this.sessionCon, this.dbConn);
    
    WorkflowProgress wfProgress = (WorkflowProgress)progressDAO.getObjectByID(workflowProgressID);
    WorkflowStep wfStep = (WorkflowStep)stepDAO.getObjectByID(wfProgress.getWorkflowStepID());
    
    List userActorList = userActorDAO.getListByWorkflowStepID(wfProgress.getWorkflowStepID());
    //get the actor for dynamic assign owner.
    List assignActorList = this.getUserRecordIDListForDynamicAssignment(wfProgress.getID());
    
    if(!Utility.isEmpty(assignActorList)){
      actorUserIDList.addAll(assignActorList);
    } else {
      log.debug("============= assign owner when create step ===========");
      //handel the Start Workflow Step, The First Step
      if ( Utility.isEmpty(userActorList) ){
        log.debug("======= First Step =======");
        Integer trackSubmitter = progressDAO.getUserIDForWorkflowTrackSubmitter(wfProgress.getTrackID());
        if (trackSubmitter != null ) {
          if(actorUserIDList.indexOf(trackSubmitter) < 0 ){
            actorUserIDList.add(trackSubmitter);
          }
        }
      }
      
      for(int i=0; i<userActorList.size(); i++){
        MtmWorkflowStepUserActor stepUserActor = (MtmWorkflowStepUserActor)userActorList.get(i);
        //workflow submitter
        if(MtmWorkflowStepUserActor.ACTOR_TYPE_SUBMITTER.equals(stepUserActor.getActorType())){
          log.debug("======= workflow submitter =======");
          Integer trackSubmitter = progressDAO.getUserIDForWorkflowTrackSubmitter(wfProgress.getTrackID());
          if (trackSubmitter != null ) {
            if(actorUserIDList.indexOf(trackSubmitter) < 0 ){
              actorUserIDList.add(trackSubmitter);
            }
          }
          
          //Submitter's Report To
        } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO.equals(stepUserActor.getActorType())){
          log.debug("======= Submitter's Report To =======");
          Integer trackSubmitter = progressDAO.getUserIDForWorkflowTrackSubmitter(wfProgress.getTrackID());
          UserRecord reportTo = (UserRecord) userDAO.getReportToUserByID(trackSubmitter);
          if (reportTo != null ) {
            if(actorUserIDList.indexOf(reportTo.getID()) < 0 ){
              actorUserIDList.add(reportTo.getID());
            }
          }
          
          //Previous Task Owner
        } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_PREVIOUS_TASK_OWNER.equals(stepUserActor.getActorType())){
          log.debug("======= Previous Task Owner =======");
          List parentProgressList = wfProgressTowDAO.getListByChildProgressID(wfProgress.getID());
          for ( int n = 0; n < parentProgressList.size(); n++ ){
            MtmWfProgressWfProgress  wfProgressTwo = (MtmWfProgressWfProgress)parentProgressList.get(n);
            List ownerList = this.getOwnerUserRecordIDListByWorkflowProgressID(wfProgressTwo.getParentProgressID());
            for ( int h = 0; h < ownerList.size(); h++ ){
              if ( actorUserIDList.indexOf(ownerList.get(h)) < 0 ){
                actorUserIDList.add(ownerList.get(h));
              }
            }
          }
          
          //Previous Task Action Taker
        } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_PREVIOUS_TASK_ACTION_TAKER.equals(stepUserActor.getActorType())){
          log.debug("======= Previous Task Action Taker =======");
          List parentProgressList = wfProgressTowDAO.getListByChildProgressID(wfProgress.getID());
          for ( int n = 0; n < parentProgressList.size(); n++ ){
            MtmWfProgressWfProgress  wfProgressTwo = (MtmWfProgressWfProgress)parentProgressList.get(n);
            //get the last actor taker
            WorkflowProgress tmpProgress = (WorkflowProgress)progressDAO.getObjectByID(wfProgressTwo.getParentProgressID());
            List lastCompleteUserIDList = this.getLastCompletedProgressUserRecordID(tmpProgress.getWorkflowStepID(), tmpProgress.getTrackID());
            for ( int h = 0; h < lastCompleteUserIDList.size(); h++ ){
              if ( actorUserIDList.indexOf(lastCompleteUserIDList.get(h)) < 0 ){
                actorUserIDList.add(lastCompleteUserIDList.get(h));
              }
            }
          }
          
          //Previous Task Action Tack's Report To
        } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO_OF_PREVIOUS_TASK_ACTION_TAKER.equals(stepUserActor.getActorType())){
          log.debug("======= Previous Task Action Tack's Report To =======");
          List parentProgressList = wfProgressTowDAO.getListByChildProgressID(wfProgress.getID());
          for ( int n = 0; n < parentProgressList.size(); n++ ){
            MtmWfProgressWfProgress  wfProgressTwo = (MtmWfProgressWfProgress)parentProgressList.get(n);
            //get the last actor taker's report to
            WorkflowProgress tmpProgress = (WorkflowProgress)progressDAO.getObjectByID(wfProgressTwo.getParentProgressID());
            List lastReportToIDList = this.getLastCompletedProgressUserRecordReportToID(tmpProgress.getWorkflowStepID(), tmpProgress.getTrackID());
            for ( int h = 0; h < lastReportToIDList.size(); h++ ){
              if ( actorUserIDList.indexOf(lastReportToIDList.get(h)) < 0 ){
                actorUserIDList.add(lastReportToIDList.get(h));
              }
            }
          }
          
          //Previous Action Taker & Previou Action Taker's Report To
        } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_ACTION_TAKER.equals(stepUserActor.getActorType()) ||
            MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO_OF_ACTION_TAKER.equals(stepUserActor.getActorType()) ){
          log.debug("======= Previous Action Taker & Previou Action Taker's Report To =======");
          List progressUserRecordList = progressUserRecordDAO.getPendingAndInProgressObjectListByWorkflowProgressID(workflowProgressID);
          Iterator it = progressUserRecordList.iterator();
          while(it.hasNext()){
            MtmWorkflowProgressUserRecord progressUserRecord = (MtmWorkflowProgressUserRecord)it.next();
            if(actorUserIDList.indexOf(progressUserRecord.getUserRecordID()) < 0 ){
              actorUserIDList.add(progressUserRecord.getUserRecordID());
            }
          }
          
         //everyOne
        } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_EVERYONE.equals(stepUserActor.getActorType()) ){
          log.debug("======= action type=everyone =======");
          List progressUserRecordList = userDAO.getFullList();
          Iterator it = progressUserRecordList.iterator();
          while(it.hasNext()){
            UserRecord userRecord = (UserRecord)it.next();
            if(actorUserIDList.indexOf(userRecord.getID()) < 0 ){
              actorUserIDList.add(userRecord.getID());
            }
          }
          //Filter by
        } else if(!Utility.isEmpty(wfStep.getFilterBy())){
          log.debug("======= Filter by  =======");
          actorUserIDList = this.getFilterByUserRecordIDListByWorkflowStepID(wfProgress.getWorkflowStepID());
          
          //Direct Assign Owner when create step.
        } else {
          log.debug("======= Direct Assign Owner when create step.  =======");
          WorkflowRetrievalManager retrievalManager = new WorkflowRetrievalManager(this.sessionCon, this.dbConn);
          List tmpUserRecordIDList = retrievalManager.getUserRecordIDListForStepAssignment(wfProgress.getWorkflowStepID());
          Iterator it = tmpUserRecordIDList.iterator();
          while(it.hasNext()){
            Integer tmpUserID = (Integer)it.next();
            if(actorUserIDList.indexOf(tmpUserID) < 0 ){
              actorUserIDList.add(tmpUserID);
            }
          }
        }
        
      }
    }
    log.debug("======= actorUserIDList ======"+actorUserIDList.size());
    // drop the users of being deleted.
    List userRecordIDList = new ArrayList();
    for(int i=0; i<actorUserIDList.size(); i++){
      log.debug("=========actorUserIDList.get(i)======="+actorUserIDList.get(i));
      UserRecord userRecord =(UserRecord)userDAO.getObjectByID((Integer)actorUserIDList.get(i));
      //log.debug("====== userRecord ======="+userRecord);
      if(!Utility.isEmpty(userRecord)){
        userRecordIDList.add(userRecord.getID());
      }
    }
    log.debug("=========== actor user ID list size ==========" + userRecordIDList.size());
    return userRecordIDList;
    
  }
  
  /**
   * get owner Id of step which not in progress yet
   * @param workflowStepID
   * @param trackID
   * @return
   * @throws ApplicationException
   */
  public List getOwnerUserRecordIDListByWorkflowStepIDAndTrackID(Integer workflowStepID,Integer trackID) throws ApplicationException {
    List actorUserIDList = new ArrayList();
    
    UserRecordDAObject userDAO = new UserRecordDAObject(this.sessionCon, this.dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(this.sessionCon, this.dbConn);
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(this.sessionCon, this.dbConn);
    MtmWorkflowStepUserActorDAObject userActorDAO = new MtmWorkflowStepUserActorDAObject(this.sessionCon, this.dbConn);
    MtmWorkflowProgressUserRecordDAObject progressUserRecordDAO = new MtmWorkflowProgressUserRecordDAObject(this.sessionCon, this.dbConn);
    MtmWfProgressWfProgressDAObject wfProgressTowDAO = new MtmWfProgressWfProgressDAObject(this.sessionCon, this.dbConn);
    
    WorkflowStep wfStep = (WorkflowStep)stepDAO.getObjectByID(workflowStepID);
    
    List userActorList = userActorDAO.getListByWorkflowStepID(workflowStepID);
     
      log.debug("============= assign owner when create step ===========");
      //handel the Start Workflow Step, The First Step
      if ( Utility.isEmpty(userActorList) ){
        log.debug("======= First Step =======");
        Integer trackSubmitter = progressDAO.getUserIDForWorkflowTrackSubmitter(trackID);
        if (trackSubmitter != null ) {
          if(actorUserIDList.indexOf(trackSubmitter) < 0 ){
            actorUserIDList.add(trackSubmitter);
          }
        }
      }
      
      for(int i=0; i<userActorList.size(); i++){
        MtmWorkflowStepUserActor stepUserActor = (MtmWorkflowStepUserActor)userActorList.get(i);
        //workflow submitter
        if(MtmWorkflowStepUserActor.ACTOR_TYPE_SUBMITTER.equals(stepUserActor.getActorType())){
          log.debug("======= workflow submitter =======");
          Integer trackSubmitter = progressDAO.getUserIDForWorkflowTrackSubmitter(trackID);
          if (trackSubmitter != null ) {
            if(actorUserIDList.indexOf(trackSubmitter) < 0 ){
              actorUserIDList.add(trackSubmitter);
            }
          }
          
          //Submitter's Report To
        } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO.equals(stepUserActor.getActorType())){
          log.debug("======= Submitter's Report To =======");
          Integer trackSubmitter = progressDAO.getUserIDForWorkflowTrackSubmitter(trackID);
          UserRecord reportTo = (UserRecord) userDAO.getReportToUserByID(trackSubmitter);
          if (reportTo != null ) {
            if(actorUserIDList.indexOf(reportTo.getID()) < 0 ){
              actorUserIDList.add(reportTo.getID());
            }
          }
          
          //Previous Task Owner
        } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_PREVIOUS_TASK_OWNER.equals(stepUserActor.getActorType())){
          log.debug("======= Previous Task Owner =======");
          //When is not progress yet,this type User is null
          
          //Previous Task Action Taker
        } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_PREVIOUS_TASK_ACTION_TAKER.equals(stepUserActor.getActorType())){
          log.debug("======= Previous Task Action Taker =======");
          //When is not progress yet,this type User is null
          
          //Previous Task Action Tack's Report To
        } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO_OF_PREVIOUS_TASK_ACTION_TAKER.equals(stepUserActor.getActorType())){
          log.debug("======= Previous Task Action Tack's Report To =======");
          //When is not progress yet,this type User is null
          
          //n step's Action Taker
        } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_ACTION_TAKER.equals(stepUserActor.getActorType())){
          log.debug("======= Previous Action Taker & Previou Action Taker's Report To =======");
          
          List workflowProgressList = progressDAO.getListByStepIDAndTrackID(stepUserActor.getActorID(), trackID);
          if (!Utility.isEmpty(workflowProgressList)) {
            WorkflowProgress workflowProgress = (WorkflowProgress)workflowProgressList.get(workflowProgressList.size()-1);
            // List parentProgressList = wfProgressTowDAO.getListByChildProgressID(workflowProgress.getID());
            List progressUserRecordList = progressUserRecordDAO.getFinishedProgressListByWorkflowProgressID(workflowProgress.getID());
            Iterator it = progressUserRecordList.iterator();
            while(it.hasNext()){
              MtmWorkflowProgressUserRecord progressUserRecord = (MtmWorkflowProgressUserRecord)it.next();
              if(actorUserIDList.indexOf(progressUserRecord.getUserRecordID()) < 0 ){
                actorUserIDList.add(progressUserRecord.getUserRecordID());
              }
            }
          } 
         //n step's actionTaker report to
        } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO_OF_ACTION_TAKER.equals(stepUserActor.getActorType())){
          log.debug("======= Step'n Action Taker report to  =======");
          UserRecordDAObject userRecordDAO = new UserRecordDAObject(sessionCon, dbConn);
          List workflowProgressList = progressDAO.getListByStepIDAndTrackID(stepUserActor.getActorID(), trackID);
          if (!Utility.isEmpty(workflowProgressList)) {
            WorkflowProgress workflowProgress = (WorkflowProgress)workflowProgressList.get(workflowProgressList.size()-1);
            // List parentProgressList = wfProgressTowDAO.getListByChildProgressID(workflowProgress.getID());
            List progressUserRecordList = progressUserRecordDAO.getFinishedProgressListByWorkflowProgressID(workflowProgress.getID());
            Iterator it = progressUserRecordList.iterator();
            while(it.hasNext()){
              MtmWorkflowProgressUserRecord progressUserRecord = (MtmWorkflowProgressUserRecord)it.next();
              AbstractBaseObject userRecord = userRecordDAO.getReportToUserByID( progressUserRecord.getUserRecordID());
              if(!Utility.isEmpty(userRecord) && actorUserIDList.indexOf(userRecord.getID()) < 0 ){
                actorUserIDList.add(userRecord.getID());
              }
            }
          }
          
         //everyOne 
        }else if(MtmWorkflowStepUserActor.ACTOR_TYPE_EVERYONE.equals(stepUserActor.getActorType()) ){
          log.debug("======= action type=everyone =======");
          List progressUserRecordList = userDAO.getFullList();
          Iterator it = progressUserRecordList.iterator();
          while(it.hasNext()){
            UserRecord userRecord = (UserRecord)it.next();
            if(actorUserIDList.indexOf(userRecord.getID()) < 0 ){
              actorUserIDList.add(userRecord.getID());
            }
          }
          //Filter by
        } else if(!Utility.isEmpty(wfStep.getFilterBy())){
          log.debug("======= Filter by  =======");
          actorUserIDList = this.getFilterByUserRecordIDListByWorkflowStepID(workflowStepID);
          
          //Direct Assign Owner when create step.
        } else {
          log.debug("======= Direct Assign Owner when create step.  =======");
          WorkflowRetrievalManager retrievalManager = new WorkflowRetrievalManager(this.sessionCon, this.dbConn);
          List tmpUserRecordIDList = retrievalManager.getUserRecordIDListForStepAssignment(workflowStepID);
          Iterator it = tmpUserRecordIDList.iterator();
          while(it.hasNext()){
            Integer tmpUserID = (Integer)it.next();
            if(actorUserIDList.indexOf(tmpUserID) < 0 ){
              actorUserIDList.add(tmpUserID);
            }
          }
        }
        
      }
   
    log.debug("======= actorUserIDList ======"+actorUserIDList.size());
    // drop the users of being deleted.
    List userRecordIDList = new ArrayList();
    for(int i=0; i<actorUserIDList.size(); i++){
      log.debug("=========actorUserIDList.get(i)======="+actorUserIDList.get(i));
      UserRecord userRecord =(UserRecord)userDAO.getObjectByID((Integer)actorUserIDList.get(i));
      //log.debug("====== userRecord ======="+userRecord);
      if(!Utility.isEmpty(userRecord)){
        userRecordIDList.add(userRecord.getID());
      }
    }
    log.debug("=========== actor user ID list size ==========" + userRecordIDList.size());
    return userRecordIDList;
    
  }

  /**
   * Gets a List of user record ID that is able to interrupt the given workflow record
   * at the given workflow progress
   *
   * @param wfProgress The current progress
   * @param recordID The master workflow record ID
   * @return List of Integer storing the user record ID
   * @throws ApplicationException
   */
  public List getInterruptActorIDListByProgressIDAndRecordID(WorkflowProgress wfProgress, Integer recordID) throws ApplicationException {
    List actorUserIDList = new ArrayList();
    
    UserRecordDAObject userDAO = new UserRecordDAObject(this.sessionCon, this.dbConn);
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(this.sessionCon, this.dbConn);
    MtmWorkflowInterruptUserActorDAObject mtmInterruptUserActorDAO = new MtmWorkflowInterruptUserActorDAObject(this.sessionCon, this.dbConn);
    MtmWorkflowProgressUserRecordDAObject progressUserRecordDAO = new MtmWorkflowProgressUserRecordDAObject(this.sessionCon, this.dbConn);
    MtmWfProgressWfProgressDAObject wfProgressTowDAO = new MtmWfProgressWfProgressDAObject(this.sessionCon, this.dbConn);
    MtmUserRecordUserRoleDAObject mtmUserRoleDAO = new MtmUserRecordUserRoleDAObject(this.sessionCon, this.dbConn);
    MtmUserRecordUserGroupDAObject mtmUserGroupDAO = new MtmUserRecordUserGroupDAObject(this.sessionCon, this.dbConn);
    
    List mtmInterruptUserActorList = mtmInterruptUserActorDAO.getListByWorkflowRecordID(recordID);
    
    
    for(int i=0; i<mtmInterruptUserActorList.size(); i++){
      MtmWorkflowInterruptUserActor InterruptUserActor = (MtmWorkflowInterruptUserActor)mtmInterruptUserActorList.get(i);
      //      Actor Type is User
      if (GlobalConstant.SUBJECT_TYPE_USER.equals(InterruptUserActor.getActorType())) {
        actorUserIDList.add(InterruptUserActor.getActorID());
        // Actor Type is Role
      } else if (GlobalConstant.SUBJECT_TYPE_ROLE.equals(InterruptUserActor.getActorType())) {
        List userRoleIDList = new ArrayList();
        userRoleIDList.add(InterruptUserActor.getActorID());
        List userIDList = mtmUserRoleDAO.getUserIDListByUserRoleList(userRoleIDList);
        for (int j=0; j< userIDList.size(); j++) {
          Integer tmpUserRoleID = (Integer)userIDList.get(j);
          if(actorUserIDList.indexOf(tmpUserRoleID) < 0){
            actorUserIDList.add(tmpUserRoleID);
          }
        }
        // Actor Type is Group
      } else if (GlobalConstant.SUBJECT_TYPE_GROUP.equals(InterruptUserActor.getActorType())) {
        List userGroupIDList = new ArrayList();
        userGroupIDList.add(InterruptUserActor.getActorID());
        List userRecordIDList = mtmUserGroupDAO.getUserIDListByUserGroupIDList(userGroupIDList);
        for (int k=0; k<userRecordIDList.size(); k++) {
          Integer tmpUserGroupID = (Integer)userRecordIDList.get(k);
          if(actorUserIDList.indexOf(tmpUserGroupID) < 0 ){
            actorUserIDList.add(tmpUserGroupID);
          }
        }
        //workflow submitter
      } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_SUBMITTER.equals(InterruptUserActor.getActorType())){
        log.debug("======= workflow submitter =======");
        Integer trackSubmitter = progressDAO.getUserIDForWorkflowTrackSubmitter(wfProgress.getTrackID());
        if (trackSubmitter != null ) {
          if(actorUserIDList.indexOf(trackSubmitter) < 0 ){
            actorUserIDList.add(trackSubmitter);
          }
        }
        
        //Submitter's Report To
      } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO.equals(InterruptUserActor.getActorType())){
        log.debug("======= Submitter's Report To =======");
        Integer trackSubmitter = progressDAO.getUserIDForWorkflowTrackSubmitter(wfProgress.getTrackID());
        UserRecord reportTo = (UserRecord) userDAO.getReportToUserByID(trackSubmitter);
        if (reportTo != null ) {
          if(actorUserIDList.indexOf(reportTo.getID()) < 0 ){
            actorUserIDList.add(reportTo.getID());
          }
        }
        
        //Previous Task Owner
      } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_PREVIOUS_TASK_OWNER.equals(InterruptUserActor.getActorType())){
        log.debug("======= Previous Task Owner =======");
        List parentProgressList = wfProgressTowDAO.getListByChildProgressID(wfProgress.getID());
        for ( int n = 0; n < parentProgressList.size(); n++ ){
          MtmWfProgressWfProgress  wfProgressTwo = (MtmWfProgressWfProgress)parentProgressList.get(n);
          List ownerList = this.getOwnerUserRecordIDListByWorkflowProgressID(wfProgressTwo.getParentProgressID());
          for ( int h = 0; h < ownerList.size(); h++ ){
            if ( actorUserIDList.indexOf(ownerList.get(h)) < 0 ){
              actorUserIDList.add(ownerList.get(h));
            }
          }
        }
        
        //Previous Task Action Taker
      } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_PREVIOUS_TASK_ACTION_TAKER.equals(InterruptUserActor.getActorType())){
        log.debug("======= Previous Task Action Taker =======");
        List parentProgressList = wfProgressTowDAO.getListByChildProgressID(wfProgress.getID());
        for ( int n = 0; n < parentProgressList.size(); n++ ){
          MtmWfProgressWfProgress  wfProgressTwo = (MtmWfProgressWfProgress)parentProgressList.get(n);
          //get the last actor taker
          WorkflowProgress tmpProgress = (WorkflowProgress)progressDAO.getObjectByID(wfProgressTwo.getParentProgressID());
          List lastCompleteUserIDList = this.getLastCompletedProgressUserRecordID(tmpProgress.getWorkflowStepID(), tmpProgress.getTrackID());
          for ( int h = 0; h < lastCompleteUserIDList.size(); h++ ){
            if ( actorUserIDList.indexOf(lastCompleteUserIDList.get(h)) < 0 ){
              actorUserIDList.add(lastCompleteUserIDList.get(h));
            }
          }
        }
        
        //Previous Task Action Tack's Report To
      } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO_OF_PREVIOUS_TASK_ACTION_TAKER.equals(InterruptUserActor.getActorType())){
        log.debug("======= Previous Task Action Tack's Report To =======");
        List parentProgressList = wfProgressTowDAO.getListByChildProgressID(wfProgress.getID());
        for ( int n = 0; n < parentProgressList.size(); n++ ){
          MtmWfProgressWfProgress  wfProgressTwo = (MtmWfProgressWfProgress)parentProgressList.get(n);
          //get the last actor taker's report to
          WorkflowProgress tmpProgress = (WorkflowProgress)progressDAO.getObjectByID(wfProgressTwo.getParentProgressID());
          List lastReportToIDList = this.getLastCompletedProgressUserRecordReportToID(tmpProgress.getWorkflowStepID(), tmpProgress.getTrackID());
          for ( int h = 0; h < lastReportToIDList.size(); h++ ){
            if ( actorUserIDList.indexOf(lastReportToIDList.get(h)) < 0 ){
              actorUserIDList.add(lastReportToIDList.get(h));
            }
          }
        }
        
        //Previous Action Taker & Previou Action Taker's Report To
      } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_ACTION_TAKER.equals(InterruptUserActor.getActorType()) ||
          MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO_OF_ACTION_TAKER.equals(InterruptUserActor.getActorType()) ){
        log.debug("======= Previous Action Taker & Previou Action Taker's Report To =======");
        List progressUserRecordList = progressUserRecordDAO.getPendingAndInProgressObjectListByWorkflowProgressID(wfProgress.getID());
        Iterator it = progressUserRecordList.iterator();
        while(it.hasNext()){
          MtmWorkflowProgressUserRecord progressUserRecord = (MtmWorkflowProgressUserRecord)it.next();
          if(actorUserIDList.indexOf(progressUserRecord.getUserRecordID()) < 0 ){
            actorUserIDList.add(progressUserRecord.getUserRecordID());
          }
        }
        //everyOne
      } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_EVERYONE.equals(InterruptUserActor.getActorType()) ){
        log.debug("======= action type=everyone =======");
        List progressUserRecordList = userDAO.getFullList();
        Iterator it = progressUserRecordList.iterator();
        while(it.hasNext()){
          UserRecord userRecord = (UserRecord)it.next();
          if(actorUserIDList.indexOf(userRecord.getID()) < 0 ){
            actorUserIDList.add(userRecord.getID());
          }
        }
        //Filter by
      }
      
    }
    
    log.debug("=========== actor user ID list size ==========" + actorUserIDList.size());
    return actorUserIDList;
    
  }
  
  /**
   *
   * @param actorType
   * @param actorID
   * @param trackID
   * @return
   * @throws ApplicationException
   */
  
  public List getActorIDListByProgressIDAndRecordID(String actorType, Integer actorID, Integer trackID) throws ApplicationException {
    List actorUserIDList = new ArrayList();
    
    UserRecordDAObject userDAO = new UserRecordDAObject(this.sessionCon, this.dbConn);
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(this.sessionCon, this.dbConn);
    MtmWorkflowProgressUserRecordDAObject progressUserRecordDAO = new MtmWorkflowProgressUserRecordDAObject(this.sessionCon, this.dbConn);
    MtmWfProgressWfProgressDAObject wfProgressTowDAO = new MtmWfProgressWfProgressDAObject(this.sessionCon, this.dbConn);
    MtmUserRecordUserRoleDAObject mtmUserRoleDAO = new MtmUserRecordUserRoleDAObject(this.sessionCon, this.dbConn);
    MtmUserRecordUserGroupDAObject mtmUserGroupDAO = new MtmUserRecordUserGroupDAObject(this.sessionCon, this.dbConn);
    WorkflowProgressDAObject workflowProgressDAO = new WorkflowProgressDAObject(this.sessionCon, this.dbConn);
    
    if (GlobalConstant.SUBJECT_TYPE_USER.equals(actorType)) {
      actorUserIDList.add(actorID);
      // Actor Type is Role
    } else if (GlobalConstant.SUBJECT_TYPE_ROLE.equals(actorType)) {
      List userRoleIDList = new ArrayList();
      userRoleIDList.add(actorID);
      List userIDList = mtmUserRoleDAO.getUserIDListByUserRoleList(userRoleIDList);
      for (int j=0; j< userIDList.size(); j++) {
        Integer tmpUserRoleID = (Integer)userIDList.get(j);
        if(actorUserIDList.indexOf(tmpUserRoleID) < 0){
          actorUserIDList.add(tmpUserRoleID);
        }
      }
      // Actor Type is Group
    } else if (GlobalConstant.SUBJECT_TYPE_GROUP.equals(actorType)) {
      List userGroupIDList = new ArrayList();
      userGroupIDList.add(actorID);
      List userRecordIDList = mtmUserGroupDAO.getUserIDListByUserGroupIDList(userGroupIDList);
      for (int k=0; k<userRecordIDList.size(); k++) {
        Integer tmpUserGroupID = (Integer)userRecordIDList.get(k);
        if(actorUserIDList.indexOf(tmpUserGroupID) < 0 ){
          actorUserIDList.add(tmpUserGroupID);
        }
      }
      //workflow submitter
    } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_SUBMITTER.equals(actorType)){
      log.debug("======= workflow submitter =======");
      Integer trackSubmitter = progressDAO.getUserIDForWorkflowTrackSubmitter(trackID);
      if (trackSubmitter != null ) {
        if(actorUserIDList.indexOf(trackSubmitter) < 0 ){
          actorUserIDList.add(trackSubmitter);
        }
      }
      
      //Submitter's Report To
    } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO.equals(actorType)){
      log.debug("======= Submitter's Report To =======");
      Integer trackSubmitter = progressDAO.getUserIDForWorkflowTrackSubmitter(trackID);
      UserRecord reportTo = (UserRecord) userDAO.getReportToUserByID(trackSubmitter);
      if (reportTo != null ) {
        if(actorUserIDList.indexOf(reportTo.getID()) < 0 ){
          actorUserIDList.add(reportTo.getID());
        }
      }
      
      //Previous Task Owner
     } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_PREVIOUS_TASK_OWNER.equals(actorType)){
      log.debug("======= Previous Task Owner =======");
      List workflowProgressList = workflowProgressDAO.getListByStepIDAndTrackID(actorID, trackID);
      if (!Utility.isEmpty(workflowProgressList)) {
        WorkflowProgress workflowProgress = (WorkflowProgress)workflowProgressList.get(workflowProgressList.size()-1);
        List parentProgressList = wfProgressTowDAO.getListByChildProgressID(workflowProgress.getID());
        for ( int n = 0; n < parentProgressList.size(); n++ ){
          MtmWfProgressWfProgress  wfProgressTwo = (MtmWfProgressWfProgress)parentProgressList.get(n);
          List ownerList = this.getOwnerUserRecordIDListByWorkflowProgressID(wfProgressTwo.getParentProgressID());
          for ( int h = 0; h < ownerList.size(); h++ ){
            if ( actorUserIDList.indexOf(ownerList.get(h)) < 0 ){
              actorUserIDList.add(ownerList.get(h));
            }
          }
        }
      }
      
      //Previous Task Action Taker
    } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_PREVIOUS_TASK_ACTION_TAKER.equals(actorType)){
      log.debug("======= Previous Task Action Taker =======");
      List workflowProgressList = workflowProgressDAO.getListByStepIDAndTrackID(actorID, trackID);
      if (!Utility.isEmpty(workflowProgressList)) {
        WorkflowProgress workflowProgress = (WorkflowProgress)workflowProgressList.get(workflowProgressList.size()-1);
        List parentProgressList = wfProgressTowDAO.getListByChildProgressID(workflowProgress.getID());
        for ( int n = 0; n < parentProgressList.size(); n++ ){
          MtmWfProgressWfProgress  wfProgressTwo = (MtmWfProgressWfProgress)parentProgressList.get(n);
          //get the last actor taker
          WorkflowProgress tmpProgress = (WorkflowProgress)progressDAO.getObjectByID(wfProgressTwo.getParentProgressID());
          List lastCompleteUserIDList = this.getLastCompletedProgressUserRecordID(tmpProgress.getWorkflowStepID(), tmpProgress.getTrackID());
          for ( int h = 0; h < lastCompleteUserIDList.size(); h++ ){
            if ( actorUserIDList.indexOf(lastCompleteUserIDList.get(h)) < 0 ){
              actorUserIDList.add(lastCompleteUserIDList.get(h));
            }
          }
        }
      }
      
      //Previous Task Action Tack's Report To
    } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO_OF_PREVIOUS_TASK_ACTION_TAKER.equals(actorType)){
      log.debug("======= Previous Task Action Tack's Report To =======");    
      List workflowProgressList = workflowProgressDAO.getListByStepIDAndTrackID(actorID, trackID);
      if (!Utility.isEmpty(workflowProgressList)) {
        WorkflowProgress workflowProgress = (WorkflowProgress)workflowProgressList.get(workflowProgressList.size()-1);
        List parentProgressList = wfProgressTowDAO.getListByChildProgressID(workflowProgress.getID());
        for ( int n = 0; n < parentProgressList.size(); n++ ){
          MtmWfProgressWfProgress  wfProgressTwo = (MtmWfProgressWfProgress)parentProgressList.get(n);
          //get the last actor taker's report to
          WorkflowProgress tmpProgress = (WorkflowProgress)progressDAO.getObjectByID(wfProgressTwo.getParentProgressID());
          List lastReportToIDList = this.getLastCompletedProgressUserRecordReportToID(tmpProgress.getWorkflowStepID(), tmpProgress.getTrackID());
          for ( int h = 0; h < lastReportToIDList.size(); h++ ){
            if ( actorUserIDList.indexOf(lastReportToIDList.get(h)) < 0 ){
              actorUserIDList.add(lastReportToIDList.get(h));
            }
          }
        }
      }
      
      //Previous Action Taker & Previou Action Taker's Report To
    } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_ACTION_TAKER.equals(actorType)){
      log.debug("======= Step'n Action Taker =======");
      List workflowProgressList = workflowProgressDAO.getListByStepIDAndTrackID(actorID, trackID);
      if (!Utility.isEmpty(workflowProgressList)) {
        WorkflowProgress workflowProgress = (WorkflowProgress)workflowProgressList.get(workflowProgressList.size()-1);
        // List parentProgressList = wfProgressTowDAO.getListByChildProgressID(workflowProgress.getID());
        List progressUserRecordList = progressUserRecordDAO.getFinishedProgressListByWorkflowProgressID(workflowProgress.getID());
        Iterator it = progressUserRecordList.iterator();
        while(it.hasNext()){
          MtmWorkflowProgressUserRecord progressUserRecord = (MtmWorkflowProgressUserRecord)it.next();
          if(actorUserIDList.indexOf(progressUserRecord.getUserRecordID()) < 0 ){
            actorUserIDList.add(progressUserRecord.getUserRecordID());
          }
        }
      }
    } else if (MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO_OF_ACTION_TAKER.equals(actorType)) {
      log.debug("======= Step'n Action Taker report to  =======");
      UserRecordDAObject userRecordDAO = new UserRecordDAObject(sessionCon, dbConn);
      List workflowProgressList = workflowProgressDAO.getListByStepIDAndTrackID(actorID, trackID);
      if (!Utility.isEmpty(workflowProgressList)) {
        WorkflowProgress workflowProgress = (WorkflowProgress)workflowProgressList.get(workflowProgressList.size()-1);
        // List parentProgressList = wfProgressTowDAO.getListByChildProgressID(workflowProgress.getID());
        List progressUserRecordList = progressUserRecordDAO.getFinishedProgressListByWorkflowProgressID(workflowProgress.getID());
        Iterator it = progressUserRecordList.iterator();
        while(it.hasNext()){
          MtmWorkflowProgressUserRecord progressUserRecord = (MtmWorkflowProgressUserRecord)it.next();
          AbstractBaseObject userRecord = userRecordDAO.getReportToUserByID( progressUserRecord.getUserRecordID());
          if(!Utility.isEmpty(userRecord) && actorUserIDList.indexOf(userRecord.getID()) < 0 ){
            actorUserIDList.add(userRecord.getID());
          }
        }
      }
    } else if (MtmWfStepNotificationTarget.TARGET_TYPE_OWNER.equals(actorType)) {
      log.debug("======= Step 'n Owner   =======");
      List workflowProgressList = workflowProgressDAO.getListByStepIDAndTrackID(actorID, trackID);
      if (!Utility.isEmpty(workflowProgressList)){
        WorkflowProgress workflowProgress = (WorkflowProgress)workflowProgressList.get(workflowProgressList.size()-1);
        //List parentProgressList = wfProgressTowDAO.getListByChildProgressID(workflowProgress.getID());
        List ownerList = this.getOwnerUserRecordIDListByWorkflowProgressID(workflowProgress.getID());
        log.debug("the owner list size::" + ownerList.size() );
        for ( int h = 0; h < ownerList.size(); h++ ){
          if ( actorUserIDList.indexOf(ownerList.get(h)) < 0 ){
            actorUserIDList.add(ownerList.get(h));
          }
        }
      }else{//not in progress
        List ownerList = this.getOwnerUserRecordIDListByWorkflowStepIDAndTrackID(actorID, trackID);
        log.debug("the owner list size::" + ownerList.size() );
        for ( int h = 0; h < ownerList.size(); h++ ){
          if ( actorUserIDList.indexOf(ownerList.get(h)) < 0 ){
            actorUserIDList.add(ownerList.get(h));
          }
        }
      }
      //everyOne
    } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_EVERYONE.equals(actorType) ){
      log.debug("======= action type=everyone =======");
      List progressUserRecordList = userDAO.getFullList();
      Iterator it = progressUserRecordList.iterator();
      while(it.hasNext()){
        UserRecord userRecord = (UserRecord)it.next();
        if(actorUserIDList.indexOf(userRecord.getID()) < 0 ){
          actorUserIDList.add(userRecord.getID());
        }
      }
    }
    log.debug("=========== actor user ID list size ==========" + actorUserIDList.size());
    return actorUserIDList;
    
  }
  
  
  /**
   * Gets a List of user record ID that is dynamically assigned as an owner
   * of the current progress by previous task
   *
   * @param workflowProgressID The current progress ID
   * @return List of Integer storing the user record ID
   * @throws ApplicationException
   */
  public List getUserRecordIDListForDynamicAssignment(Integer workflowProgressID) throws ApplicationException {
    //    MtmWorkflowStepUserActorDAObject userActorDAO = new MtmWorkflowStepUserActorDAObject(this.sessionCon, this.dbConn);
    MtmWorkflowDynamicUserActorDAObject dynamicUserActorDAO = new MtmWorkflowDynamicUserActorDAObject(this.sessionCon, this.dbConn);
    MtmUserRecordUserRoleDAObject mtmUserRoleDAO = new MtmUserRecordUserRoleDAObject(sessionCon, dbConn);
    MtmUserRecordUserGroupDAObject mtmUserGroupDAO = new MtmUserRecordUserGroupDAObject(sessionCon, dbConn);
    MtmUserGroupUserRoleDAObject mtmGroupRoleDAO = new MtmUserGroupUserRoleDAObject(sessionCon, dbConn);
    
    List actorUserIDList = new ArrayList();
    //    WorkflowProgress wfProgress = (WorkflowProgress)progressDAO.getObjectByID(workflowProgressID);
    //    List assignActorList = userActorDAO.getListByWorkflowStepID(wfProgress.getWorkflowStepID(), MtmWorkflowStepUserActor.ACTOR_TYPE_RUNTIME_ASSIGN);
    
    //    if(!Utility.isEmpty(assignActorList)){
    log.debug("========== Dynamic assigned owner at runtime ==========");
    log.debug("=======progressID:========"+workflowProgressID);
    // dynamic assign owner at runtime;
    List dynamicAssignActorList = dynamicUserActorDAO.getListByWorkflowProgressID(workflowProgressID);
    for(int i=0; i<dynamicAssignActorList.size(); i++){
      MtmWorkflowDynamicUserActor dynamicUserActor = (MtmWorkflowDynamicUserActor)dynamicAssignActorList.get(i);
      // dynamic assign user
      if(MtmWorkflowStepUserActor.ACTOR_TYPE_USER.equals(dynamicUserActor.getActorType())){
        if(actorUserIDList.indexOf(dynamicUserActor.getActorID()) < 0 ){
          actorUserIDList.add(dynamicUserActor.getActorID());
        }
        // dynamic assign group
      } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_GROUP.equals(dynamicUserActor.getActorType())){
        List userGroupIDList = new ArrayList();
        userGroupIDList.add(dynamicUserActor.getActorID());
        List userRecordIDList = mtmUserGroupDAO.getUserIDListByUserGroupIDList(userGroupIDList);
        for (int k=0; k<userRecordIDList.size(); k++) {
          Integer tmpUserGroupID = (Integer)userRecordIDList.get(k);
          if(actorUserIDList.indexOf(tmpUserGroupID) < 0 ){
            actorUserIDList.add(tmpUserGroupID);
          }
        }
        // dynamic assign role
        
        
      } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_ROLE.equals(dynamicUserActor.getActorType())){
        List userRoleIDList = new ArrayList();
        userRoleIDList.add(dynamicUserActor.getActorID());
        
        //get userID form Mtm_User_Record_User_Role table
        List userIDList = mtmUserRoleDAO.getUserIDListByUserRoleList(userRoleIDList);
        for (int j=0; j< userIDList.size(); j++) {
          Integer tmpUserRoleID = (Integer)userIDList.get(j);
          if(actorUserIDList.indexOf(tmpUserRoleID) < 0){
            actorUserIDList.add(tmpUserRoleID);
          }
        }
        //get groupID from Mtm_User_Group_User_Role table
        List roleAry = new ArrayList();
        roleAry.add(dynamicUserActor.getActorID());
        List tmpGroupAry = mtmGroupRoleDAO.getListByUserRoleAry(roleAry);
        for (int j = 0; j < tmpGroupAry.size(); j++) {
          MtmUserGroupUserRole tmpUserGroupUserRole = (MtmUserGroupUserRole)tmpGroupAry.get(j);
          //get UserID from Mtm_User_Record_User_Group table
          List groupIDList = new ArrayList();
          groupIDList.add(tmpUserGroupUserRole.getUserGroupID());
          List userRecordIDList = mtmUserGroupDAO.getUserIDListByUserGroupIDList(groupIDList);
          for (int k=0; k<userRecordIDList.size(); k++) {
            Integer tmpUserGroupID = (Integer)userRecordIDList.get(k);
            if(actorUserIDList.indexOf(tmpUserGroupID) < 0 ){
              actorUserIDList.add(tmpUserGroupID);
            }
          }
        }
      }
    }
    
    //    }
    log.debug("======= Dynamic assigned owner user list size =========" + actorUserIDList.size());
    return actorUserIDList;
  }
  
  /**
   * Determines if the FormRecord of the given ID is associated to any workflow record
   *
   * @param formRecordID The form record ID
   * @return boolean true if the form is associated with any workflow
   * @throws ApplicationException
   * @see com.dcivision.form.bean.FormRecord
   */
  public boolean isAssociatedWorkflow(Integer formRecordID) throws ApplicationException {
    MtmWorkflowRecordObjectLinkDAObject objectLinkDAO = new MtmWorkflowRecordObjectLinkDAObject(this.sessionCon, this.dbConn);
    FormRecordDAObject formDAO = new FormRecordDAObject(this.sessionCon, this.dbConn);
    
    List workflowList = objectLinkDAO.getListByObjectIDWrokflowCategoryID(formRecordID, new Integer(SystemWorkflowConstant.FMB_SUBMISSION_BY_FORM), GlobalConstant.OBJECT_TYPE_FORM);
    if(Utility.isEmpty(workflowList)){
      FormRecord formRecord = (FormRecord)formDAO.getObjectByID(formRecordID);
      workflowList = objectLinkDAO.getListByObjectIDWrokflowCategoryID(formRecord.getFormGroupID(), new Integer(SystemWorkflowConstant.FMB_SUBMISSION_BY_GROUP), GlobalConstant.OBJECT_TYPE_FORMGROUP);
    }
    
    if(!Utility.isEmpty(workflowList)){
      return true;
    }
    return false;
  }
  
  /**
   * Checks if the 2nd step of the workflow of the current task need to dynamically assign owner
   *
   * @param currentProgress The current progress
   * @return true if dynamic assign owner for 2nd step
   * @throws ApplicationException
   */
  public boolean checkAssignSecondStepOwner(WorkflowProgress currentProgress) throws ApplicationException {
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionCon, dbConn);
    MtmWorkflowStepUserActorDAObject  workflowStepUserActorDAO =new MtmWorkflowStepUserActorDAObject(sessionCon,dbConn);
    WorkflowStep currentStep = (WorkflowStep)stepDAO.getObjectByID(currentProgress.getWorkflowStepID());
    boolean isAssignNextOwner = false;
    List nextStepList = stepDAO.getListByParentStepID(currentProgress.getWorkflowStepID());
    try {
      if(!Utility.isEmpty(nextStepList)){
        if (WorkflowStep.ACTION_TYPE_SUBMIT.equals(currentStep.getActionType())){
          for (int h = 0; h < nextStepList.size(); h++) {
            WorkflowStep tmpWfStep = (WorkflowStep)nextStepList.get(h);
            List mtmWorkflowStepUserActorList = workflowStepUserActorDAO.getListByWorkflowStepIDAndActorType(tmpWfStep.getID(),MtmWorkflowStepUserActor.ACTOR_TYPE_RUNTIME_ASSIGN);
            if (!Utility.isEmpty(mtmWorkflowStepUserActorList) && mtmWorkflowStepUserActorList.size() > 0){
              isAssignNextOwner = true;
              
            }
          }
        }
      }
    } catch (ApplicationException appEx) {
      throw appEx;
    } catch (Exception e) {
      log.error("Error when executing automatic task.", e);
      throw new ApplicationException(e);
    }
    return isAssignNextOwner;
  }
  
  
  /**
   * Checks if the next step of the current task need to dynamically assign owner
   *
   * @param maintForm The MaintWorkflowProgressForm object containing the current progress data
   * @return true if dynamic assign owner for next step
   * @throws ApplicationException
   */
  public boolean checkAssignNextStepOwner(MaintWorkflowProgressForm maintForm) throws ApplicationException {
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionCon, dbConn);
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionCon, dbConn);
    MtmWorkflowStepUserActorDAObject  workflowStepUserActorDAO =new MtmWorkflowStepUserActorDAObject(sessionCon,dbConn);
    WorkflowStep currentStep = (WorkflowStep)stepDAO.getObjectByID(new Integer(maintForm.getWorkflowStepID()));
    boolean isAssignNextOwner = false;
    String currentStatus = maintForm.getStatus();
    String decision[] = maintForm.getDecision();
    
    try {
      if (!Utility.isEmpty(currentStatus) && (Utility.isEmpty(decision))){
        if (WorkflowProgress.STATUS_GENERAL_ACCOMPLISHMENT.equals(currentStatus)){
          currentStatus = WorkflowProgress.STATUS_SUBMIT;
        }
        List nextStepList = stepStepDAO.getListByParentIDChildIDAndParentType(currentStep.getID(),null, currentStatus);
        if (!Utility.isEmpty(nextStepList)){
          for (int h = 0; h < nextStepList.size(); h++) {
            MtmWorkflowStepWorkflowStep mtmWorkflowStepWorkflowStep = (MtmWorkflowStepWorkflowStep)nextStepList.get(h);
            MtmWorkflowStepUserActor mtmWorkflowStepUserActor = workflowStepUserActorDAO.getWorkflowStepUserActorByWorkflowStepID(mtmWorkflowStepWorkflowStep.getChildStepID());
            if(MtmWorkflowStepUserActor.ACTOR_TYPE_RUNTIME_ASSIGN.equals(mtmWorkflowStepUserActor.getActorType())){
              isAssignNextOwner = true;
            }
          }
        }
      } else if (!Utility.isEmpty(decision)){
        for (int i =0; i<decision.length; i++){
          List nextStepList = stepStepDAO.getListByParentIDChildIDAndParentType(currentStep.getID(),null, decision[i]);
          if (!Utility.isEmpty(nextStepList)){
            for (int h = 0;h < nextStepList.size();h++) {
              MtmWorkflowStepWorkflowStep mtmWorkflowStepWorkflowStep = (MtmWorkflowStepWorkflowStep)nextStepList.get(h);
              MtmWorkflowStepUserActor mtmWorkflowStepUserActor = workflowStepUserActorDAO.getWorkflowStepUserActorByWorkflowStepID(mtmWorkflowStepWorkflowStep.getChildStepID());
              if(MtmWorkflowStepUserActor.ACTOR_TYPE_RUNTIME_ASSIGN.equals(mtmWorkflowStepUserActor.getActorType())){
                isAssignNextOwner = true;
              }
            }
          }
        }
      }
    } catch (ApplicationException appEx) {
      throw appEx;
    } catch (Exception e) {
      log.error("Error when executing automatic task.", e);
      throw new ApplicationException(e);
    }
    return isAssignNextOwner;
  }
  
  /**
   * handel schedule job.
   * @param assignPriorityStepMap
   * @throws ApplicationException
   */
  private void handelScheduleJob(
      HashMap assignPriorityStepMap) throws ApplicationException{
    Set keySet = assignPriorityStepMap.keySet();
    Iterator iterator = keySet.iterator();
    while (iterator.hasNext()) {
      Integer stepID = (Integer) iterator.next();
      WorkflowProgress workflowProgress = (WorkflowProgress) assignPriorityStepMap.get(stepID);
      
      log.debug("----------- Create Schedule For Progress Priority Begin -----------");
      this.createScheduleForProgressPriority(workflowProgress.getID(),this.sessionCon, this.dbConn );
      log.debug("----------- Create Schedule For Progress Priority End  -----------");
      
    }
  }
  
  
  /**
   * Creates schedule job for priority escalation
   * @param wfProgressID
   * @param sessionConstainer
   * @param dbConn
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.MtmWorkflowProgressPriorityRule
   */
  public void createScheduleForProgressPriority(
      Integer wfProgressID,
      SessionContainer sessionConstainer,
      Connection dbConn) throws ApplicationException {
    MtmWorkflowProgressPriorityRuleDAObject  progressPriorityRuleDAO = new MtmWorkflowProgressPriorityRuleDAObject(sessionConstainer, dbConn);
    MtmWorkflowStepPriorityRuleDAObject  stepPriorityRuleDAO = new MtmWorkflowStepPriorityRuleDAObject(sessionConstainer, dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionConstainer, dbConn);
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(sessionConstainer, dbConn);
    
    WorkflowProgress wfProgress = (WorkflowProgress)progressDAO.getObjectByID(wfProgressID);
    
    WorkflowStep wfStep = (WorkflowStep)stepDAO.getObjectByID(wfProgress.getWorkflowStepID());
    List priorityRuleList = progressPriorityRuleDAO.getListWorkflowProgressID(wfProgressID);
    
    Timestamp dueDate = null;
    Timestamp arrivedDate = null;
    //count the due date.
    if(!Utility.isEmpty(wfProgress.getDueDate())){
      dueDate = wfProgress.getDueDate();
    }
    log.debug("Workflop Progress Due Date Is " + dueDate);
    
    //get the arrived Date
    arrivedDate = wfProgress.getCreateDate();
    log.debug("Workflop Progress Arrived Date Is " + arrivedDate);
    
    if(!Utility.isEmpty(priorityRuleList)){
      log.debug("Wrokflow Progress Dynamci Assign Priority Rules.");
      for(int i=0; i<priorityRuleList.size(); i++){
        MtmWorkflowProgressPriorityRule priorityRule = (MtmWorkflowProgressPriorityRule)priorityRuleList.get(i);
        Timestamp triggerTimestamp = null;
        //After task arrived notification.
        if(MtmWorkflowProgressPriorityRule.AFTER_TASK_ARRIVED.equals(priorityRule.getEscalateTrigger())){
          log.debug("Trigger Type Is After Task Arrived Notification.");
          triggerTimestamp =timestampAdd ( arrivedDate, priorityRule.getEscalateAtField(), priorityRule.getEscalateAtAmt() );
        }
        //Befor due date notification.
        if(MtmWorkflowProgressPriorityRule.BEFORE_DUE_DATE.equals(priorityRule.getEscalateTrigger())){
          log.debug("Trigger Type Is Befor Due Date Notification.");
          if(Utility.isEmpty(dueDate)){
            continue;
          }
          triggerTimestamp =timestampMinus ( dueDate, priorityRule.getEscalateAtField(), priorityRule.getEscalateAtAmt() );
        }
        this.createScheduleJobNotification(wfProgressID.toString(), priorityRule.getID().toString(),MtmWorkflowProgressPriorityRule.OBJECT_TYPE_BY_PROGRESS, sessionConstainer.getUserRecordID(), triggerTimestamp );
      }
    } else {
      log.debug("Workflow Progress Priority Be Changed According To Step's Priority Rules.");
      List stepPriorityRuleList = stepPriorityRuleDAO.getListByWorkflowStepID(wfStep.getID());
      for(int j=0; j<stepPriorityRuleList.size(); j++){
        MtmWorkflowStepPriorityRule stepPriorityRule = (MtmWorkflowStepPriorityRule)stepPriorityRuleList.get(j);
        List currentStepList=stepDAO.getListCurrentStepByStepID(stepPriorityRule.getWorkflowStepID());//simon_add
        if(currentStepList!=null && currentStepList.size()>0){//simon_add
          Timestamp triggerTimestamp = null;
          //After task arrived notification.
          if(MtmWorkflowProgressPriorityRule.AFTER_TASK_ARRIVED.equals(stepPriorityRule.getEscalateTrigger())){
            log.debug("Trigger Type Is After Task Arrived Notification.");
            triggerTimestamp =timestampAdd ( arrivedDate, stepPriorityRule.getEscalateAtField(), stepPriorityRule.getEscalateAtAmt() );
          }
          //Befor due date notification.
          if(MtmWorkflowProgressPriorityRule.BEFORE_DUE_DATE.equals(stepPriorityRule.getEscalateTrigger())){
            log.debug("Trigger Type Is Befor Due Date Notification.");
            if(Utility.isEmpty(dueDate)){
              continue;
            }
            triggerTimestamp =timestampMinus ( dueDate, stepPriorityRule.getEscalateAtField(), stepPriorityRule.getEscalateAtAmt() );
          }
          this.createScheduleJobNotification(wfProgressID.toString(), stepPriorityRule.getID().toString(), MtmWorkflowProgressPriorityRule.OBJECT_TYPE_BY_STEP, sessionConstainer.getUserRecordID(), triggerTimestamp);
        }//simon_add
      }
    }
    
  }
  
  /**
   * Creates notification when the task is arrived, which includes,
   * arrival, pending for, before due, overdue
   *
   * @param wfProgressID The current progress ID
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.MtmWfStepNotificationRule
   */
  public void createNotificationWhenTaskArrived(Integer wfProgressID) throws ApplicationException {
    MtmWfStepNotificationRuleDAObject notificationRuleDAO = new MtmWfStepNotificationRuleDAObject(sessionCon, dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionCon, dbConn);
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(sessionCon, dbConn);
    
    WorkflowProgress wfProgress = (WorkflowProgress)progressDAO.getObjectByID(wfProgressID);
    Timestamp dueDate = wfProgress.getDueDate();
    Timestamp arrivedDate = wfProgress.getCreateDate();
    
    List stepNotificationRuleList = notificationRuleDAO.getListByWorkflowStepID(wfProgress.getWorkflowStepID());
    for (int j = 0; j < stepNotificationRuleList.size(); j++ ) {
      MtmWfStepNotificationRule stepNotificationRule = (MtmWfStepNotificationRule)stepNotificationRuleList.get(j);
      List currentStepList=stepDAO.getListCurrentStepByStepID(stepNotificationRule.getWorkflowStepID());//simon_add
      if(currentStepList!=null && currentStepList.size()>0){//simon_add
        // If it's a due date related rule and the task don't have due date, look for the next rule
        if ( (MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_BEFORE_DUE_DATE.equals(stepNotificationRule.getNotifyTrigger()) ||
            MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_OVER_DUE_DATE.equals(stepNotificationRule.getNotifyTrigger())) &&
            Utility.isEmpty(dueDate) ) {
          continue;
        }
        
        // Get the time this notification has to been triggered
        Timestamp triggerTimestamp = null;
        if(MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_ARRIVED.equals(stepNotificationRule.getNotifyTrigger()) && WorkflowProgress.STATUS_PENDING.equals(wfProgress.getStatus())){
          triggerTimestamp = timestampAdd ( arrivedDate, stepNotificationRule.getNotifyAtField(), stepNotificationRule.getNotifyAtAmt());
        }
        if(MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_PENDING_FOR.equals(stepNotificationRule.getNotifyTrigger()) && WorkflowProgress.STATUS_PENDING.equals(wfProgress.getStatus())){
          triggerTimestamp = timestampAdd ( arrivedDate, stepNotificationRule.getNotifyAtField(), stepNotificationRule.getNotifyAtAmt());
        }
        if(MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_BEFORE_DUE_DATE.equals(stepNotificationRule.getNotifyTrigger())){
          triggerTimestamp = timestampMinus ( dueDate, stepNotificationRule.getNotifyAtField(), stepNotificationRule.getNotifyAtAmt());
        }
        if(MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_OVER_DUE_DATE.equals(stepNotificationRule.getNotifyTrigger())){
          triggerTimestamp = timestampAdd ( dueDate, stepNotificationRule.getNotifyAtField(), stepNotificationRule.getNotifyAtAmt());
        }
        // Finished getting trigger time
        WorkflowStep currentWorkflowStep = (WorkflowStep)stepDAO.getObjectByID(stepNotificationRule.getWorkflowStepID());
        this.createScheduleJobForNotificationRule(wfProgress, currentWorkflowStep,  sessionCon.getUserRecordID(), triggerTimestamp, stepNotificationRule);
      }//simon_add
    }
  }
  
  
  /**
   * Creates notification when the task is stoped
   *
   * @param wfProgressID The current progress ID
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.MtmWfStepNotificationRule
   */
  public void createNotificationWhenTaskStoped(Integer wfProgressID) throws ApplicationException {
    MtmWfStepNotificationRuleDAObject notificationRuleDAO = new MtmWfStepNotificationRuleDAObject(sessionCon, dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionCon, dbConn);
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(sessionCon, dbConn);
    
    WorkflowProgress wfProgress = (WorkflowProgress)progressDAO.getObjectByID(wfProgressID);
    Timestamp stopTime = Utility.getCurrentTimestamp();
    
    List stepNotificationRuleList = notificationRuleDAO.getListByWorkflowStepID(wfProgress.getWorkflowStepID());
    for (int j = 0; j < stepNotificationRuleList.size(); j++ ) {
      MtmWfStepNotificationRule stepNotificationRule = (MtmWfStepNotificationRule)stepNotificationRuleList.get(j);
      List currentStepList=stepDAO.getListCurrentStepByStepID(stepNotificationRule.getWorkflowStepID());//simon_add
      
      if(currentStepList!=null && currentStepList.size()>0){    
        // Get the time this notification has to been triggered
        Timestamp triggerTimestamp = null;
        if(MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_STOPPED.equals(stepNotificationRule.getNotifyTrigger())
            && WorkflowProgress.STATUS_CANCEL.equals(wfProgress.getStatus())){
          triggerTimestamp = timestampAdd ( stopTime, stepNotificationRule.getNotifyAtField(), stepNotificationRule.getNotifyAtAmt());
          
          // Finished getting trigger time
          WorkflowStep currentWorkflowStep = (WorkflowStep)stepDAO.getObjectByID(stepNotificationRule.getWorkflowStepID());
          this.createScheduleJobForNotificationRule(wfProgress, currentWorkflowStep,  sessionCon.getUserRecordID(), triggerTimestamp, stepNotificationRule);
        }       
      }
    }
  }
  
  
  /**
   * Creates notification when a task is updated, which includes,
   * completed/checkout/stopped/interrupted?
   *
   * @param workflowProgress The current progress
   * @param triggerType The notification trigger type
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.MtmWfStepNotificationRule
   */
  private void createNotificationWhenTaskUpdated(WorkflowProgress workflowProgress,List triggerType) throws ApplicationException {
    MtmWfStepNotificationRuleDAObject notificationRuleDAO = new MtmWfStepNotificationRuleDAObject(this.sessionCon, this.dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(this.sessionCon, this.dbConn);
    Timestamp updateDate = workflowProgress.getUpdateDate();
    List stepNotificationRuleList = notificationRuleDAO.getListByWorkflowStepIDAndNotifyTrigger( workflowProgress.getWorkflowStepID(), triggerType );
    for (int j = 0; j < stepNotificationRuleList.size(); j++ ) {
      MtmWfStepNotificationRule stepNotificationRule = (MtmWfStepNotificationRule)stepNotificationRuleList.get(j);
      Timestamp triggerTimestamp = timestampAdd ( updateDate, stepNotificationRule.getNotifyAtField(), stepNotificationRule.getNotifyAtAmt() );
      List currentStepList=stepDAO.getListCurrentStepByStepID(stepNotificationRule.getWorkflowStepID());//simon_add
      if(currentStepList!=null && currentStepList.size()>0){//simon_add
        WorkflowStep currentWorkflowStep = (WorkflowStep)stepDAO.getObjectByID(stepNotificationRule.getWorkflowStepID());
        this.createScheduleJobForNotificationRule(workflowProgress, currentWorkflowStep,  this.sessionCon.getUserRecordID(), triggerTimestamp, stepNotificationRule);
      }//simon_add
    }
  }
  
  /**
   * Creates schedule job for notify user about the priority escalation.
   *
   * @param workflowProgressID The current workflow progress ID
   * @param objectID The priority rule ID
   * @param objectType The escalate type
   * @param userRecordID The user record ID that act as the sender of notification
   * @param triggerTime The exact time this notification is to be sent
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.MtmWorkflowProgressPriorityRule
   */
  private void createScheduleJobNotification(
      String workflowProgressID,
      String objectID,
      String objectType,
      Integer userRecordID,
      java.util.Date triggerTime
  ) throws ApplicationException {
    
    org.quartz.SimpleTrigger trigger = null;
    
    try {
      log.info("scheduledJobVec.size(): " );
      org.quartz.Scheduler sched = com.dcivision.framework.SchedulerFactory.getScheduler();
      
      //logger.info("******** Adding schedule for Alert on = "+updateAlert.getObjectType()+ " object ID = " + updateAlert.getObjectID());
      
      org.quartz.JobDataMap dataMap = new org.quartz.JobDataMap();
      dataMap.put("workflowProgressID", workflowProgressID);
      dataMap.put("objectID", objectID);
      dataMap.put("objectType", objectType);
      dataMap.put("userRecordID", userRecordID.toString());
      
      org.quartz.JobDetail job = new org.quartz.JobDetail(
          GlobalConstant.JOB_PREFIX_WORKFLOW_PRIORITY + MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_PRIORITY_CHANGED + "_" + objectID + "_" + workflowProgressID,
          GlobalConstant.JOB_PREFIX_WORKFLOW_PRIORITY + "GROUP",
          TimeHandleProgressPriority.class);
      job.setJobDataMap(dataMap);
      
      log.info("******** Schedule date: " + triggerTime.toString());
      trigger = new org.quartz.SimpleTrigger(GlobalConstant.JOB_PREFIX_WORKFLOW_PRIORITY + MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_PRIORITY_CHANGED + "_" + objectID + "_" + workflowProgressID,
          GlobalConstant.JOB_PREFIX_WORKFLOW_PRIORITY + "GROUP",
          triggerTime, null,0,0);
      
      trigger.setMisfireInstruction(org.quartz.SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
      log.info("******** Trigger created");
      log.info(job.getFullName() + " will run at: " + trigger.getNextFireTime() + " & repeat: " + trigger.getRepeatCount() + "/" + trigger.getRepeatInterval());
      
      sched.scheduleJob(job, trigger);
      log.info("******** Trigger created");
      sched = null;
    } catch (Exception e) {
      log.error("Error in Scheduler.", e);
      throw new ApplicationException(ErrorConstant.SYS_SCHEDULER_ERROR);
    }
  }
  /**
   * Creates schedule job for the rule-based notifications
   *
   * @param workflowProgress The related workflow progress
   * @param currentProgress The current workflow progress
   * @param workflowStep The workflow step
   * @param triggerType The trigger type
   * @param ownerType The owner type
   * @param userRecordID The user record ID
   * @param triggerTime The exact notification time
   * @param stepNotificationRule The notification rule object
   * @param targetID The target ID
   * @throws ApplicationException
   * @see com.dcivision.workflow.core.MailScheduleManager
   * @see com.dcivision.workflow.core.WorkflowOperationManager#scheduleMailByNotifyRule
   * @see com.dcivision.workflow.bean.MtmWfStepNotificationRule
   * @see com.dcivision.workflow.bean.MtmWfStepNotificationTarget
   */
  public void createScheduleJobForNotificationRule(
      WorkflowProgress currentProgress,
      WorkflowStep workflowStep,
      Integer userRecordID,
      java.util.Date triggerTime,
      MtmWfStepNotificationRule stepNotificationRule
  ) throws ApplicationException {
    WorkflowOperationManager workflowOperationManager = new WorkflowOperationManager(this.sessionCon, this.dbConn);
    java.util.Vector tmpVector = new java.util.Vector();
    tmpVector.add(workflowStep);
    tmpVector.add(triggerTime);
    currentProgress.setUpdaterID(userRecordID);
    tmpVector.add(currentProgress);
    tmpVector.add(stepNotificationRule);
    workflowOperationManager.addScheduleJobVecForNotifiyRule(tmpVector);
    workflowOperationManager.scheduleMailByNotifyRule(workflowOperationManager.getScheduledJobVector());
  }
  
  
  /**
   * insert progress priority rule into Mtm_WF_Progress_Priority_Rule table
   * @param assignPriorityStepMap
   * @param priorityRuleListValue
   * @param request
   * @throws ApplicationException
   */
  private void insertProgressPriorityRule(
      HashMap assignPriorityStepMap,
      String priorityRuleListValue
  ) throws ApplicationException{
    MtmWorkflowProgressPriorityRuleDAObject  progressPriorityRuleDAO = new MtmWorkflowProgressPriorityRuleDAObject(this.sessionCon, this.dbConn);
    
    String[] ruleArr = TextUtility.splitString(priorityRuleListValue, "|");
    
    for(int i=0; i<ruleArr.length; i++){
      String ackMethod = "";
      MtmWorkflowProgressPriorityRule priorityRule = new MtmWorkflowProgressPriorityRule();
      String[] priorityElementArr = TextUtility.splitString(ruleArr[i], ",");
      WorkflowProgress workflowProgress = (WorkflowProgress) assignPriorityStepMap.get(new Integer(priorityElementArr[0]));
      
      priorityRule.setWorkflowProgressID(workflowProgress.getID());
      priorityRule.setEscalateAtAmt(TextUtility.parseIntegerObj(priorityElementArr[1]));
      priorityRule.setEscalateAtField(TextUtility.parseIntegerObj(priorityElementArr[2]));
      priorityRule.setEscalateTrigger(priorityElementArr[3]);
      priorityRule.setEscalateLevel(TextUtility.parseIntegerObj(priorityElementArr[4]));
      
      if(!"null".equals(priorityElementArr[5])){
        ackMethod = priorityElementArr[5];
      }
      if(!"null".equals(priorityElementArr[6])) {
        if(Utility.isEmpty(ackMethod)){
          ackMethod = priorityElementArr[6];
        } else {
          ackMethod += "," + priorityElementArr[6];
        }
      }
      priorityRule.setEscalateAckMethod(ackMethod);
      progressPriorityRuleDAO.insertObject(priorityRule);
    }
    
    
  }
  
  /**
   * Gets a List of user record ID that have the same (role/group/hierarchy leve) as the
   * workflow submitter, which is specified in the workflow step's filterBy property
   * @param workflowStepID The step ID of the step that need to filter by submitter
   * @return List of matching user record ID
   * @throws ApplicationException
   */
  public List getFilterByUserRecordIDListByWorkflowStepID(Integer workflowStepID)
  throws ApplicationException {
    List userRecordIDList = new ArrayList();
    
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(this.sessionCon, this.dbConn);
    UserRecordDAObject userDAO = new UserRecordDAObject(this.sessionCon, this.dbConn );
    MtmWorkflowStepUserActorDAObject userActorDAO = new MtmWorkflowStepUserActorDAObject(this.sessionCon, this.dbConn);
    MtmUserRecordUserRoleDAObject userRoleDAO = new MtmUserRecordUserRoleDAObject(this.sessionCon, this.dbConn);
    MtmUserRecordUserGroupDAObject userGroupDAO = new MtmUserRecordUserGroupDAObject(this.sessionCon, this.dbConn);
    
    WorkflowStep wfStep = (WorkflowStep)stepDAO.getObjectByID(workflowStepID);
    List userActorList = userActorDAO.getListByWorkflowStepID(workflowStepID);
    log.debug("=========== filter by userActorList ====="+userActorList.size());
    
    List groupIDList = new ArrayList();
    List roleIDList = new ArrayList();
    for(int i = 0; i < userActorList.size(); i++){
      MtmWorkflowStepUserActor userActor = ( MtmWorkflowStepUserActor)userActorList.get(i);
      //log.debug("========== userActor ====="+userActor.getID() +":"+userActor.getActorType());
      if(MtmWorkflowStepUserActor.ACTOR_TYPE_GROUP.equals(userActor.getActorType())){
        groupIDList.add(userActor.getActorID());
      } else if(MtmWorkflowStepUserActor.ACTOR_TYPE_ROLE.equals(userActor.getActorType())){
        roleIDList.add(userActor.getActorID());
      }
    }
    log.debug("========= filter by userGroupID ======="+groupIDList.size());
    log.debug("========= filter by roleIDList ======="+roleIDList.size());
    
    WorkflowRetrievalManager retrievalManager = new WorkflowRetrievalManager(this.sessionCon, this.dbConn);
    List tmpUserRecordIDList = retrievalManager.getUserRecordIDListForStepAssignment(workflowStepID);
    
    if (Utility.isEmpty(wfStep.getFilterBy())){
      return null;
    }
    log.debug("==== Step.getFilterBy ======"+wfStep.getFilterBy());
    if (wfStep.getFilterBy().intValue() > 0 && !Utility.isEmpty(tmpUserRecordIDList) ){//filter by staff level (USER_DEF_*)
      userRecordIDList = userDAO.filterUserRecordIDByListLevelIndex(tmpUserRecordIDList, wfStep.getFilterBy().toString());
    } else if (wfStep.getFilterBy().intValue() == -1 && !Utility.isEmpty(groupIDList)) {//filter by group
      userRecordIDList = userGroupDAO.getUserIDListByUserGroupIDList(groupIDList);
    } else if (wfStep.getFilterBy().intValue() == -2 && !Utility.isEmpty(roleIDList)) {//filter by role
      userRecordIDList = userRoleDAO.getUserRecordIDListByUserRoleIDList(roleIDList);
    }
    log.debug("====== filterBy userReocrdID ======"+userRecordIDList.size());
    return userRecordIDList;
  }
  
  
  /**
   * Gets the route for form value driven task by workflow step ID & track ID
   * @param workflowStepID The workflow step ID
   * @param trackID The workflow task's trackID
   * @return Array of String storing the routes
   * @throws ApplicationException
   */
  private String[] getDecisionPointForFormValueDrivenTaskByWorkflowStepIDTrackID(
      Integer workflowStepID,
      Integer trackID  ) throws ApplicationException{
    String[] decisionPoint = null;
    List decisionList = new ArrayList();
    String defaultRoute = null;
    boolean parseException = false;
    
    WorkflowStepFormEquatDAObject formEquatDAO = new WorkflowStepFormEquatDAObject(this.sessionCon, this.dbConn);
    
    List formEquatList = formEquatDAO.getListByWorkflowStepID(workflowStepID);
    for(int i=0; i<formEquatList.size(); i++){
      WorkflowStepFormEquat  formEquat = (WorkflowStepFormEquat)formEquatList.get(i);
      
      if(GlobalConstant.TRUE.equals(formEquat.getDefaultFlag())){
        defaultRoute = WorkflowStep.PARENT_TYPE_SINGLE + (formEquat.getRouteSeq().intValue()-1 );
        //Is Number Operation
      } else if(WorkflowStepFormEquat.OPERATION_TYPE_NUMBER.equals(formEquat.getOperationType())){
        try{
          if (this.numberCompare(formEquat, trackID)){
            decisionList.add(WorkflowStep.PARENT_TYPE_SINGLE + (formEquat.getRouteSeq().intValue()-1 ) );
          }
        } catch (Exception e){
          log.debug("Number Parse Exception");
          parseException = true;
        }
        
        //Is Date Operation
      } else if(WorkflowStepFormEquat.OPERATION_TYPE_DATE.equals(formEquat.getOperationType())){
        try {
          if(this.dateCompare(formEquat, trackID)){
            decisionList.add(WorkflowStep.PARENT_TYPE_SINGLE + (formEquat.getRouteSeq().intValue()-1 ) );
          }
        } catch (Exception e){
          log.debug("Date Parse Exception");
          parseException = true;
        }
        
        //Is String Operation
      } else if(WorkflowStepFormEquat.OPERATION_TYPE_STRING.equals(formEquat.getOperationType())){
        try {
          if(this.stringCompare(formEquat, trackID)){
            decisionList.add(WorkflowStep.PARENT_TYPE_SINGLE + (formEquat.getRouteSeq().intValue()-1 ) );
          }
        } catch (Exception e){
          log.debug("String Parse Exception");
          parseException = true;
        }
      }
      
    }
    
    if(Utility.isEmpty(decisionList) ){
      if(parseException){
        this.autoRoutedToDefault = WorkflowProgress.ROUTED_TO_DEFAULT;
      }
      decisionPoint = new String[1];
      decisionPoint[0] = defaultRoute;
    } else {
      if(parseException){
        this.autoRoutedToDefault = WorkflowProgress.FORM_EQUATION_ERROR;
      }
      /* Commented by Brook.Huang.
       * Since existing several equal decision cause lots of problem, so we limited type of
       * Form Driven Step can just transfer to single branch.
       * Logic: If there are more than one equal branch, choise the first only.
      decisionPoint = new String[decisionList.size()];
      for(int n=0; n<decisionList.size(); n++){
        log.debug("---decisionList.get(n)----"+decisionList.get(n).toString());
        decisionPoint[n] = decisionList.get(n).toString();
      }
      */
      
      // Just transfer to the first branch for stable version.
      decisionPoint = new String[]{decisionList.get(0).toString()};
    }
    log.debug("==== decisionPoint ====" + decisionPoint);
    
    
    return decisionPoint;
  }
  
  /**
   * Gets the form data by trackID and formElementID
   * @param trackID The workflow track ID
   * @param formElementID The form element ID
   * @return The form data of the specified field
   * @throws ApplicationException
   * @see com.dcivision.form.bean.FormElement
   * @see com.dcivision.form.bean.FormData
   */
  private String getFormDataByTrackIDFormElementID(Integer trackID, Integer formElementID ) throws ApplicationException{
    Integer formSubmissionID = null;
    log.debug("====trackID,formElementID===="+trackID+":"+formElementID);
    MtmWorkflowProgressSystemObjectDAObject systemObjectDAO = new MtmWorkflowProgressSystemObjectDAObject(this.sessionCon, this.dbConn);
    FormDataElementDAObject fdeDAO = new FormDataElementDAObject(this.sessionCon, this.dbConn);
    
    //get the Form submission ID
    /**
     * @TODO To get the element data value by formRecordID, formPageID, formSectionID.
     *       and the MtmWorkflowProgressSystemObject by trackID, object ID
     */
    MtmWorkflowProgressSystemObject sysObject = null;
    List objectList = systemObjectDAO.getListByTrackID(trackID);
    if(!Utility.isEmpty(objectList)){
      sysObject = (MtmWorkflowProgressSystemObject)objectList.get(0);  
    }
    //MtmWorkflowProgressSystemObject sysObject = (MtmWorkflowProgressSystemObject)systemObjectDAO.getObjectByTrackID(trackID);
    formSubmissionID = sysObject.getObjectID();
    String result = "";
    
    List elementDataList = fdeDAO.getElementValueListBySubmissionIDElementID(formSubmissionID,formElementID);
    if(!Utility.isEmpty(elementDataList)){
      for (int i=0; i<elementDataList.size(); i++) {
        FormData formData = (FormData)elementDataList.get(i);
        if ( i>0 ) {
          result += ",";
        }
        result += formData.getDataString();
      }
    }
    
    return result;
  }
  
  /**
   * Number Compare
   * 
   * @param formEquat
   * @param trackID The workflow track ID
   * @return boolean
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.WorkflowStepFormEquat
   */
  private boolean numberCompare(WorkflowStepFormEquat formEquat, Integer trackID) throws ApplicationException{
    boolean result = false;
    //gain the expression
    String expression = "";
    expression = formEquat.getOperand1() + " " + formEquat.getCompare() + " " + formEquat.getOperand2();
    log.debug("======== expressions1: ======="+expression);
    expression = TextUtility.replaceString(expression, "!!!", "+");
    expression = TextUtility.replaceString(expression, "|", "");
    log.debug("======== expressions2: ======="+expression);
    
    //gain the parameter name & parameter value
    String elementID = null;
    List parameterList = new ArrayList();
    
    String[] itemArr = TextUtility.splitString(formEquat.getOperand1()+formEquat.getOperand2(), "|");
    for(int j=0; j<itemArr.length; j++){
      if(itemArr[j].startsWith(WorkflowStepFormEquat.ELEMENT_PREFIX_CHAR)){
        String[] paramItem = new String[2];
        elementID = itemArr[j].substring(WorkflowStepFormEquat.ELEMENT_PREFIX_CHAR.length());//e.g. itemArr[j] = "E100" ,then elementID = "100".
        //@ToDo: get form element data
        paramItem[0] = itemArr[j];
        paramItem[1] = this.getFormDataByTrackIDFormElementID(trackID,new Integer(elementID));
        log.debug("==== paramItem: ====" + paramItem[0] + ";" + paramItem[1]);
        parameterList.add(paramItem);
      }
    }
    
    String[][] parameterValueArr = new String[parameterList.size()][2];
    for(int k=0; k<parameterList.size(); k++){
      parameterValueArr[k] = (String[])parameterList.get(k);
    }
    
    result = this.evaluateExpression(expression, parameterValueArr );
    
    return result;
    
  }
  
  /**
   * Date Compare
   * 
   * @param formEquat
   * @param trackID The workflow track ID
   * @return boolean
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.WorkflowStepFormEquat
   */
  private boolean dateCompare(WorkflowStepFormEquat formEquat, Integer trackID) throws ApplicationException{
    boolean result = false;
    
    String dateFormat = formEquat.getDateFormat();
    String operand1 = formEquat.getOperand1();
    String operand2 = formEquat.getOperand2();
    Timestamp operand1Timestamp = null;
    Timestamp operand2Timestamp = null;
    
    operand1 = TextUtility.replaceString(operand1, "|", "");
    operand2 = TextUtility.replaceString(operand2, "|", "");
    log.debug("====== dateCompare old operand1:operand2 ======"+operand1+":"+operand2);
    if(operand1.startsWith(WorkflowStepFormEquat.ELEMENT_PREFIX_CHAR)){
      operand1 = this.getFormDataByTrackIDFormElementID(trackID,new Integer(operand1.substring(WorkflowStepFormEquat.ELEMENT_PREFIX_CHAR.length())));
      operand1Timestamp = TextUtility.parseDBDateTimeToTimeStamp(operand1);
    } else {
      operand1Timestamp = Utility.parseDateStringToSystemDate(operand1, dateFormat);
    }
    if(operand2.startsWith(WorkflowStepFormEquat.ELEMENT_PREFIX_CHAR)){
      operand2 = this.getFormDataByTrackIDFormElementID(trackID,new Integer(operand2.substring(WorkflowStepFormEquat.ELEMENT_PREFIX_CHAR.length())));
      operand2Timestamp = TextUtility.parseDBDateTimeToTimeStamp(operand2);
    } else {
      operand2Timestamp = Utility.parseDateStringToSystemDate(operand2, dateFormat);
    }
    log.debug("====== dateCompare new operand1:operand2 ======"+operand1+":"+operand2);
    log.debug("##############################################################");
    log.debug("====== operand1Timestamp:operand2Timestamp ======"+operand1Timestamp+":"+operand2Timestamp);
    
    //Before
    if(WorkflowStepFormEquat.DATE_OPERATION_BEFORE.equals(formEquat.getCompare())){
      result = operand1Timestamp.before(operand2Timestamp);
      //Equals
    } else if (WorkflowStepFormEquat.DATE_OPERATION_EQUALS.equals(formEquat.getCompare())){
      result = operand1Timestamp.equals(operand2Timestamp);
      //After
    } else if (WorkflowStepFormEquat.DATE_OPERATION_AFTER.equals(formEquat.getCompare())){
      result = operand1Timestamp.after(operand2Timestamp);
    }
    
    return result;
  }
  
  /**
   * String Compare
   * 
   * @param formEquat
   * @param trackID The workflow track ID
   * @return boolean
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.WorkflowStepFormEquat
   */
  private boolean stringCompare(WorkflowStepFormEquat formEquat, Integer trackID) throws ApplicationException{
    boolean result = false;
    String operand1 = formEquat.getOperand1();
    String operand2 = formEquat.getOperand2();
    
    operand1 = TextUtility.replaceString(operand1, "!!!", "");
    operand2 = TextUtility.replaceString(operand2, "!!!", "");
    log.debug("====== stringCompare old operand1:operand2 ======"+operand1+":"+operand2);
    String[] tmpItemArr1 = TextUtility.splitString(operand1, "|");
    operand1 = "";
    for(int j=0; j<tmpItemArr1.length; j++){
      if(tmpItemArr1[j].startsWith(WorkflowStepFormEquat.ELEMENT_PREFIX_CHAR)){
        operand1 += this.getFormDataByTrackIDFormElementID(trackID,new Integer(tmpItemArr1[j].substring(WorkflowStepFormEquat.ELEMENT_PREFIX_CHAR.length())));
      } else {
        operand1 += tmpItemArr1[j];
      }
    }
    
    String[] tmpItemArr2 = TextUtility.splitString(operand2, "|");
    operand2 = "";
    for(int j=0; j<tmpItemArr2.length; j++){
      if(tmpItemArr2[j].startsWith(WorkflowStepFormEquat.ELEMENT_PREFIX_CHAR)){
        operand2 += this.getFormDataByTrackIDFormElementID(trackID,new Integer(tmpItemArr2[j].substring(WorkflowStepFormEquat.ELEMENT_PREFIX_CHAR.length())));
      } else {
        operand2 += tmpItemArr2[j];
      }
    }
    log.debug("====== stringCompare new operand1:operand2 ======"+operand1+":"+operand2);
    //Contains
    if(WorkflowStepFormEquat.STRING_OPERATION_CONTAINS.equals(formEquat.getCompare())){
      result = operand1.indexOf(operand2) > -1;
      //Equals
    } else if(WorkflowStepFormEquat.STRING_OPERATION_EQUALS.equals(formEquat.getCompare())){
      result = operand1.equals(operand2);
      //Starts With
    } else if(WorkflowStepFormEquat.STRING_OPERATION_STARTS_WITH.equals(formEquat.getCompare())){
      result = operand1.startsWith(operand2);
    }
    
    return result;
  }
  
  /**
   * Evaluates for the form-value driven task equation
   * <pre>
   * e.g.
   *    x + y > z * 8
   * </pre>
   * @param expression The form equation
   * @param parameterValueArr The parameters
   * @return True if the expression is satisfied
   */
  public boolean evaluateExpression (String expression, String[][] parameterValueArr){
    double result ;
    log.debug("=== expression ==" + expression);
    org.nfunk.jep.JEP myParser = new org.nfunk.jep.JEP();
    myParser.addStandardFunctions();
    myParser.addStandardConstants();
    for(int i=0; i<parameterValueArr.length; i++) {
      myParser.addVariable(parameterValueArr[i][0], Double.parseDouble(parameterValueArr[i][1]));
    }
    myParser.parseExpression(expression);
    
    result = myParser.getValue();
    log.debug("==== Result: ====" + result);
    return result != 0;
    
  }
  
  /**
   * GET progress that it can not correct report to
   * @return
   * @throws ApplicationException
   */
  /*
   public List getProgressListNotCorrectReportTo() throws ApplicationException {
   List lProgressList = new ArrayList();
   
   //the actor type is MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(this.sessionCon, this.dbConn);
    List lProgressIDM = progressDAO.getProgressIDListForFailedMentor();
    for(int i=0;i<lProgressIDM.size();i++){
    WorkflowProgress workflowProgress = new WorkflowProgress();
    workflowProgress = (WorkflowProgress)progressDAO.getObjectByID((Integer)lProgressIDM.get(i));
    lProgressList.add(workflowProgress);
    }
    
    //The actor_type is ACTOR_TYPE_REPORT_TO_OF_PREVIOUS_TASK_OWNER.
     lProgressIDM = null;
     lProgressIDM = progressDAO.getProgressIDListForFailedReportToOfPreviousTask();
     for(int i=0;i<lProgressIDM.size();i++){
     WorkflowProgress workflowProgress = new WorkflowProgress();
     workflowProgress = (WorkflowProgress)progressDAO.getObjectByID((Integer)lProgressIDM.get(i));
     lProgressList.add(workflowProgress);
     }
     return(lProgressList);
     }
     */
  /**
   * Builds the previous progress relationship for current progress
   *
   * @param parentProgress The previous progress
   * @param childProgress The current progress
   * @param childchildType Specify how the current progress is created (normal,rollback,revoke)
   */
  public void insertMtmWorkflowProgressMtmWorkflowProgress(WorkflowProgress parentProgress, WorkflowProgress currentProgress, String childChildType)throws ApplicationException  {
    MtmWfProgressWfProgressDAObject mtmWfProgressWfProgressDAO = new MtmWfProgressWfProgressDAObject(this.sessionCon, this.dbConn);
    MtmWorkflowStepWorkflowStepDAObject mtmWorkflowStepWorkflowStepDAO = new MtmWorkflowStepWorkflowStepDAObject(this.sessionCon, this.dbConn);
    WorkflowProgressDAObject workflowProgressDAO = new WorkflowProgressDAObject(this.sessionCon, this.dbConn);
    MtmWfProgressWfProgress mtmWfProgressWfProgress = new MtmWfProgressWfProgress();
    try {
      if (this.checkIsMultsParentProgress(currentProgress)) {
        List stepWfStepList = mtmWorkflowStepWorkflowStepDAO.getListByChildStepIDAndLoopBack(currentProgress.getWorkflowStepID(), MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
        for (int i = 0; i < stepWfStepList.size(); i++) {
          MtmWorkflowStepWorkflowStep mtmWorkflowStepWorkflowStep = (MtmWorkflowStepWorkflowStep)stepWfStepList.get(i);
          List workflowProgressList = workflowProgressDAO.getListByStepIDAndTrackID(mtmWorkflowStepWorkflowStep.getParentStepID(), currentProgress.getTrackID());
          if (!Utility.isEmpty(workflowProgressList)){
            WorkflowProgress workflowProgress = (WorkflowProgress)workflowProgressList.get(0);
            mtmWfProgressWfProgress.setParentProgressID(workflowProgress.getID());
            mtmWfProgressWfProgress.setChildProgressID(currentProgress.getID());
            mtmWfProgressWfProgress.setChildCreateType(childChildType);
            mtmWfProgressWfProgress = (MtmWfProgressWfProgress)mtmWfProgressWfProgressDAO.insertObject(mtmWfProgressWfProgress);
          }
          
        }
      } else {
        mtmWfProgressWfProgress.setParentProgressID(parentProgress.getID());
        mtmWfProgressWfProgress.setChildProgressID(currentProgress.getID());
        mtmWfProgressWfProgress.setChildCreateType(childChildType);
        mtmWfProgressWfProgress = (MtmWfProgressWfProgress)mtmWfProgressWfProgressDAO.insertObject(mtmWfProgressWfProgress);
      }
    } catch (ApplicationException ex) {
      log.error(ex, ex);
    }
  }
  
  /**
   * Determines if the current progress is come from several previous step
   * @param workflowProgress The current progress
   * @return true if multiple previous steps
   * @throws ApplicationException
   */
  private boolean checkIsMultsParentProgress(WorkflowProgress workflowProgress)throws ApplicationException {
    MtmWorkflowStepWorkflowStepDAObject mtmWorkflowStepWorkflowStepDAO = new MtmWorkflowStepWorkflowStepDAObject(this.sessionCon, this.dbConn);
    List mtmWorkflowStepWorkflowStepList = new ArrayList();
    try {
      mtmWorkflowStepWorkflowStepList = mtmWorkflowStepWorkflowStepDAO.getListByChildStepID(workflowProgress.getWorkflowStepID());
      if (!Utility.isEmpty(mtmWorkflowStepWorkflowStepList)) {
        if (mtmWorkflowStepWorkflowStepList.size() > 1) {
          // if mtmWorkflowStepWorkflowStepList is size greater than one ,the step has more parent step
          List stepWfStepList = mtmWorkflowStepWorkflowStepDAO.getListByChildStepIDAndLoopBack(workflowProgress.getWorkflowStepID(), MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          if (!Utility.isEmpty(stepWfStepList)) {
            if (stepWfStepList.size() > 1) {
              // if stepWfStepList size greater than one ,the step is  not loopback
              return true;
            }
          }
        }
      }
    } catch (ApplicationException ex) {
      log.error(ex, ex);
    }
    return false;
  }
  
  /**
   * Given the timestamp, minus a certain amount of the given calendar field
   * @param timestamp The Timestamp object
   * @param field An Integer object representing the Calendar object's minus field
   * @param value An Integer object representing the amount to be minus
   */
  private Timestamp timestampMinus ( Timestamp timestamp, Integer field, Integer value ) {
    return timestampAdd ( timestamp, field, new Integer ( value.intValue()*-1 ) );
  }
  
  /**
   * Given the timestamp, add a certain amount of the given calendar field
   *@param timestamp The Timestamp object
   *@param field An Integer object representing the Calendar object's add field
   *@param value An Integer object representing the amount to be add
   */
  private Timestamp timestampAdd ( Timestamp timestamp, Integer field, Integer value ) {
    Calendar calendar = Utility.timestampToCalendar(timestamp);
    if( Calendar.DATE == field.intValue() ){
      calendar.add(Calendar.DATE, value.intValue());
    } else if( Calendar.HOUR == field.intValue() ){
      calendar.add(Calendar.HOUR, value.intValue());
    } else if( Calendar.MINUTE == field.intValue() ){
      calendar.add(Calendar.MINUTE, value.intValue());
    }
    return Utility.calendarToTimestamp(calendar);
  }
  /**
   * Determines if the current track is interruptable, it will check if the workflow record
   * enables the interrupt options and the current user in SessionContainer have rights
   * to interrupt the track.
   *
   * @param workflowProgress The current progress
   * @param workflowRecordID The workflow record ID that the task is belong to
   * @return true if this progress(track) can be interrupted
   * @throws ApplicationException
   */
  public boolean isInterruptAble(WorkflowProgress workflowProgress, Integer workflowRecordID,Integer userID)throws ApplicationException {
    WorkflowRecordDAObject recordDAO = new WorkflowRecordDAObject(this.sessionCon, this.dbConn);
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(this.sessionCon, this.dbConn);
    WorkflowProgressManager progressManager = new WorkflowProgressManager(this.sessionCon, this.dbConn);
    boolean interruptionCompletedWf = SystemParameterFactory.getSystemParameterBoolean(SystemParameterConstant.WORKFLOW_SUPPORT_INTERRUPTION_COMPLETED_WORKFLOW);
    
    boolean interruptAble = false;
    boolean falg = false;
    //  check allow interrupt
    if (!Utility.isEmpty(workflowRecordID)) {
      WorkflowRecord workflowRecord = (WorkflowRecord)recordDAO.getObjectByID(workflowRecordID);
      if(GlobalConstant.TRUE.equals(workflowRecord.getAllowInterrupt())) {
        List interruptActorIDList = progressManager.getInterruptActorIDListByProgressIDAndRecordID(workflowProgress, workflowRecordID);
        for (int k = 0; !Utility.isEmpty(interruptActorIDList) && k < interruptActorIDList.size(); k++) {
          Integer userActorID = (Integer)interruptActorIDList.get(k);
          if(userID.equals(userActorID)) {
            falg = true;
            break;
          }
        }
        if (falg) {// if current actor have interrupt right;
          List interruptProgressList = new ArrayList();
          if(interruptionCompletedWf){
            interruptProgressList = progressDAO.getProgressListByTrackIDAndStatus(workflowProgress.getTrackID(),null);
          }else{    
            interruptProgressList = progressDAO.getNonFinishedProgressListByTrackID (workflowProgress.getTrackID());
          }
          //List penddingProgressList = progressDAO.getNonFinishedProgressListByTrackID(workflowProgress.getTrackID());
          if (!Utility.isEmpty(interruptProgressList)) {
            interruptAble = true;
          }
        }
      }
    }
    log.debug("the interrupt button is::: " + interruptAble);
    return interruptAble;
  }
  
  /**
   * check the interrupt permission width default User
   * @param workflowProgress
   * @param workflowRecordID
   * @return
   * @throws ApplicationException
   */
  public boolean isInterruptAble(WorkflowProgress workflowProgress, Integer workflowRecordID)throws ApplicationException {
    return isInterruptAble(workflowProgress,workflowRecordID,this.sessionCon.getUserRecordID());
  }
  /**
   * check current User and his delegater
   * @param workflowProgress
   * @param workflowRecordID
   * @return
   * @throws ApplicationException
   */
  public boolean CheckInterruptAble(WorkflowProgress workflowProgress, Integer workflowRecordID)throws ApplicationException {
    boolean interruptAble = false;
    if((interruptAble=isInterruptAble(workflowProgress,workflowRecordID))){
      return interruptAble;//if current User have interrupt permission ,return true;
    }
    
    UserDelegationDAObject delegationADO = new UserDelegationDAObject(this.sessionCon,this.dbConn);
    List delegationList = delegationADO.getListDelegatedToUser();
    for(Iterator it=delegationList.iterator();it.hasNext();){
      UserDelegation userDelegation = (UserDelegation)it.next();
      interruptAble = isInterruptAble(workflowProgress,workflowRecordID,userDelegation.getSourceUserID());
      if(interruptAble){
        return interruptAble;
      }
    }
    return false;
  }
  
  /**
   * Updates the calendar's todo list if the current progress finished 
   * (It will only apply to task that have due date).
   *
   * @param workflowProgressID The current progress ID
   * @param userRecordID The current user record ID
   * @throws ApplicationException
   */
  public void editCalendarByProgressID(Integer workflowProgressID, Integer userRecordID)throws ApplicationException {
    CalendarRecordDAObject calendarRecordDAObject = new CalendarRecordDAObject(sessionCon,dbConn);
    List activeCalendarRecordList = new ArrayList();
    if (!Utility.isEmpty(userRecordID)) {
      activeCalendarRecordList = calendarRecordDAObject.getListByObjectIDAndObjectTypeNotCurrentUser(workflowProgressID, CalendarRecord.OBJECT_TYPE_WORKFLOW,  userRecordID);
    } else {
      activeCalendarRecordList = calendarRecordDAObject.getListByObjectIDAndObjectTypeNotCurrentUser(workflowProgressID, CalendarRecord.OBJECT_TYPE_WORKFLOW,  null);
    }
    for (int h = 0; !Utility.isEmpty(activeCalendarRecordList) && h < activeCalendarRecordList.size(); h++ ) {
      CalendarRecord calendarRecord = (CalendarRecord)activeCalendarRecordList.get(h);
      calendarRecord.setStatus(GlobalConstant.TRUE);
      calendarRecordDAObject.updateObject(calendarRecord);
    }
  }
  
  /**
   * recod the submit form in the start step
   * 
   * @param workflowProgressID
   * @param maintForm
   * @throws ApplicationException
   */
  private void recordSubmitForm(Integer workflowProgressID, AbstractActionForm maintForm)throws ApplicationException {
    MtmWfProgressAccessFormDAObject accessFormDAO = new MtmWfProgressAccessFormDAObject(this.sessionCon, this.dbConn);
    FormSubmissionDAObject formSubmissionDAO = new FormSubmissionDAObject(this.sessionCon, this.dbConn);
    
    MaintWorkflowRecordForm recordForm = (MaintWorkflowRecordForm)maintForm;    
    /* Commented by Brook.Huang, either save status "S" or submit status "M",
     * we should record it into MTM_WF_PROGRESS_ACCESS_FORM.
    List formSubmissionList = formSubmissionDAO.getListByTrackIDStatus( new Integer(recordForm.getTrackID() ), FormSubmission.FORM_SUBMISSION_STATU_SUBMIT);
    */
    List formSubmissionList = formSubmissionDAO.getListByTrackIDStatus(new Integer(recordForm.getTrackID()), null);
    
    for(int i=0; i<formSubmissionList.size(); i++){
      FormSubmission formSubmission = (FormSubmission)formSubmissionList.get(i);
      
      
      MtmWfProgressAccessForm accessForm = new MtmWfProgressAccessForm();
      accessForm.setWorkflowProgressID(workflowProgressID);
      accessForm.setFormRecordID(formSubmission.getFormRecordID());
      accessForm.setFormSubmissionID(formSubmission.getID());
      accessForm.setProcessed(WorkflowStep.FORM_PROCESS_PERMISSION_CHANGE);
      
      //Add by Samlin For Vera
      FormDataElementDAObject formElementDAO=new FormDataElementDAObject(this.sessionCon,this.dbConn);
      formElementDAO.UpdateProgressIDBySubmissionID(formSubmission.getID(), workflowProgressID);
      
      Object object = accessFormDAO.getObjectByWorkflowProgressIDFormRecordIDProcessed(
          accessForm.getWorkflowProgressID(),
          accessForm.getFormRecordID(),
          accessForm.getProcessed());
      //check whether it is exist
      if(Utility.isEmpty(object)){
        accessFormDAO.insertObject(accessForm);  
      }            
      
    }
  }
  
  /**
   * get the workflow accessional information by track ID
   * 
   * @param trackID The workflow track ID
   * @return HashMap
   * @throws ApplicationException
   */
  public HashMap getWorkflowAccessionalInfo(Integer trackID) throws ApplicationException {
    String comment = "";
    List attachmentList = new ArrayList();
    
    HashMap hashMap = new HashMap();
    
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(this.sessionCon, this.dbConn);
    MtmWorkflowProgressUserRecordDAObject progressUserRecordDAO = new MtmWorkflowProgressUserRecordDAObject(this.sessionCon, this.dbConn);
    WorkflowProgressAttachmentDAObject attachmentDAO = new WorkflowProgressAttachmentDAObject(this.sessionCon, this.dbConn);
    
    //get the due date
    WorkflowProgress firstProgress = (WorkflowProgress)progressDAO.getFirstProgressByTrackID(trackID);    
    //    if(!Utility.isEmpty(firstProgress.getDueDate())){
    //      dueDate = TextUtility.formatTimestampToDBDate(firstProgress.getDueDate());
    //    }
    
    if(!Utility.isEmpty(firstProgress)){
      //get the comment  
      MtmWorkflowProgressUserRecord progressUserRecord = (MtmWorkflowProgressUserRecord)progressUserRecordDAO.getObjectByWorkflowProgressIDUserRecordID(
          firstProgress.getID(), this.sessionCon.getUserRecordID());
      if(!Utility.isEmpty(progressUserRecord)){
        comment = progressUserRecord.getReason();
      }  
      
      //get the attachement file list
      attachmentList = attachmentDAO.getListByMtmWorkflowProgressUserRecordID(progressUserRecord.getID());
      
    }
    
    
    hashMap.put("comment", comment);
    hashMap.put("attachmentList", attachmentList);
    //    hashMap.put("dueDate", dueDate);
    
    return hashMap;
  }
  
  private void loopbackAllBranch(WorkflowStep currentStep, WorkflowProgress workflowProgress, String parentType) throws ApplicationException {
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(this.sessionCon, this.dbConn);
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(this.sessionCon, this.dbConn);
    WorkflowRetrievalManager wfRetrievalManager = new WorkflowRetrievalManager(this.sessionCon, this.dbConn);
    List stepAndStepList = stepStepDAO.getListByParentStepIDAndParentType(currentStep.getID(), parentType);
    for (int i = 0; !Utility.isEmpty(stepAndStepList) && i < stepAndStepList.size(); i++) {
      MtmWorkflowStepWorkflowStep stepAndStep = (MtmWorkflowStepWorkflowStep)stepAndStepList.get(i);
      if (GlobalConstant.TRUE.equals(stepAndStep.getLoopBack()) && GlobalConstant.TRUE.equals(stepAndStep.getLoopBackAllBranch())){
        List progressList = wfRetrievalManager.getInterruptedProgressList(workflowProgress.getTrackID(), stepAndStep.getChildStepID());
        for (int j = 0; !Utility.isEmpty(progressList) && j < progressList.size(); j++) {
          WorkflowProgress wfProgress = (WorkflowProgress) progressList.get(j);
          if (!wfProgress.getWorkflowStepID().equals(currentStep.getID())) {
            wfProgress.setStatus(WorkflowProgress.STATUS_ROLLBACK);
            wfProgress = (WorkflowProgress)progressDAO.updateObject(wfProgress);
          }
        }
      }
    }
  }
  
  /**
   * record the form submission for trigger a form workflow in the background. 
   * @param workflowRecordID the workflow record ID
   * @return FormSubmission
   * @throws ApplicationException
   */
  public FormSubmission recordFormSubmission(Integer workflowRecordID) throws ApplicationException {
    MtmWorkflowRecordObjectLinkDAObject objectLinkDAO = new MtmWorkflowRecordObjectLinkDAObject(this.sessionCon, this.dbConn);
    FormSubmissionDAObject formSubmissionDAO = new FormSubmissionDAObject(this.sessionCon, this.dbConn);
    
    FormSubmission formSubmission = null;
    
    Integer trackID = DataSourceFactory.getNextSequence("WORKFLOW_TRACK_ID");
    
    
    List objectLinkList = objectLinkDAO.getListByWorkflowRecordID(workflowRecordID);
    for (int i = 0; i < objectLinkList.size(); i++){
      MtmWorkflowRecordObjectLink objectLink = (MtmWorkflowRecordObjectLink)objectLinkList.get(i);
      
      Integer formRecordID = objectLink.getObjectID();
      
      formSubmission = new FormSubmission();
      formSubmission.setTrackID(trackID);
      formSubmission.setFormRecordID(formRecordID);
      formSubmission.setWorkflowRecordID(workflowRecordID);
      formSubmission.setIp(this.sessionCon.getUserIPAddress());
      formSubmission.setStatus(FormSubmission.FORM_SUBMISSION_STATU_PENDING);
      
      formSubmission = (FormSubmission)formSubmissionDAO.insertObject(formSubmission);  
      
    }
    return formSubmission;
  }
  
  public List getExecuteStepListByWorkflowRecordIDWorkflowTrackIDUserRecordID(Integer workflowTrackID, Integer UserRecordID)throws ApplicationException {
  	WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(this.sessionCon, this.dbConn);
  	WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(this.sessionCon, this.dbConn);
  	ListWorkflowProgressForm listForm = new ListWorkflowProgressForm();
  	listForm.setWorkflowTrackID(workflowTrackID.toString());
  	listForm.setShowFullList(GlobalConstant.TRUE);
  	listForm.setDelegateUserRecordID(UserRecordID.toString());
	listForm.setSortAttribute("WP.ID");
    listForm.setSortOrder("DESC");
  	listForm.setPageOffset("1000000");
  	List workflowProgressList = progressDAO.getListProcessedForUser(listForm);
    List stepList = new ArrayList();
  	for (int i = 0; i <workflowProgressList.size(); i++) {
  	 	WorkflowStep curStep = (WorkflowStep)stepDAO.getObjectByID(((WorkflowProgress)workflowProgressList.get(i)).getWorkflowStepID());
  	 	if (stepList.indexOf(curStep)< 0) {
  	 		stepList.add(curStep);
  	  }
     }
    return (stepList);
  }

  /**
   * get the only User for a AutoTask
   * @param currentProgress
   * @return
   * @throws ApplicationException
   */
  public Integer getAutoTaskActorUser(WorkflowProgress currentProgress) throws ApplicationException {
    MtmWorkflowStepUserActorDAObject stepUserDAO=new MtmWorkflowStepUserActorDAObject(sessionCon, dbConn);
    List useractorList=stepUserDAO.getListByWorkflowStepID(currentProgress.getWorkflowStepID());
    if(Utility.isEmpty(useractorList)){
      return null;
    }        
    MtmWorkflowStepUserActor tmpuserActor=(MtmWorkflowStepUserActor)useractorList.get(0);
    List actorList=getActorIDListByProgressIDAndRecordID(tmpuserActor.getActorType(),tmpuserActor.getActorID(),currentProgress.getTrackID());
    if(Utility.isEmpty(actorList)){
      return null;
    }        
    Integer userID=(Integer)actorList.get(0);
    if (userID!=null && userID.intValue()!=0){
      return userID;
    }
    return null;
  }

  /**
   * remove inexistence WorkflowProgressAttachment in the List.
   * @param tempAttachmentList all WorkflowProgressAttachment list.
   * @return an new existed WorkflowProgressAttachment List.
   * @throws ApplicationException ApplicationException.
   */
  public List removeUnexistWorkflowProgressAttachment(List tempAttachmentList)
      throws ApplicationException {
    List newAttachmentList = new ArrayList();
    DmsVersionDAObject dmsVersionDAO = new DmsVersionDAObject(sessionCon, dbConn);
    List workflowProgressAttachmentList = new ArrayList();
    for (int i = 0; i< tempAttachmentList.size(); i++) {
      WorkflowProgressAttachment attachment = (WorkflowProgressAttachment) tempAttachmentList.get(i);
      if (WorkflowProgressAttachment.ATTACHED_DMS_VERSION_SYSTEM_ID.equals(attachment.getAttachmentType()) ) {
        workflowProgressAttachmentList.add(attachment);
      } else {
        newAttachmentList.add(attachment);
      }
    }
    if (workflowProgressAttachmentList.size() > 0) {
      List existDmsVersionIDList = dmsVersionDAO.getExistDmsVersionIDByList(
          workflowProgressAttachmentList);
        if (workflowProgressAttachmentList.size() != existDmsVersionIDList.size()) {
          for (int i = 0; i < workflowProgressAttachmentList.size(); i++) {
            WorkflowProgressAttachment attachment = (WorkflowProgressAttachment) tempAttachmentList.get(i);
            for (int j = 0; j < existDmsVersionIDList.size(); j++)
            {
              if (attachment.getExternalID().equals(existDmsVersionIDList.get(j))) {
                newAttachmentList.add(attachment);
                break;
              }
            }
          }
        } else {
           return tempAttachmentList;
        }
    }
    return newAttachmentList;
  }

  /**
   * Gets an list(existed Record) of workflow attachments that is
   * uploaded by the specified progress user record
   * @param mtmWorkflowProgressUserRecordID The progress user record ID (NOT user record ID)
   * @return List of workflow progress attachment
   */
  public List getWorkflowProgressAttachmentListByMtmWorkflowProgressUserRecordID(
      Integer MtmWorkflowProgressUserRecordID) {
    WorkflowProgressAttachmentDAObject workflowProgressAttachmentDAO
           = new WorkflowProgressAttachmentDAObject(sessionCon, dbConn);
    List attList = new ArrayList();
    try {
      List attachList = workflowProgressAttachmentDAO.getListByMtmWorkflowProgressUserRecordID(MtmWorkflowProgressUserRecordID);
      attList = removeUnexistWorkflowProgressAttachment(attachList);
     } catch (ApplicationException exp) {
      log.error(exp);
    }
    return attList;
  }

  /**
   * use this method to support more than one field sorting.
   * for example:
   *   1, jsp file:
   *       {"workflow.columnheader.workflow_code", "C.WORKFLOW_CODE,C.WORKFLOW_VERSION"}.
   *       means workflow_code field sort by both WORKFLOW_CODE and WORKFLOW_VERSION.
   *   2, DAObject file:
   *       sortAttribute = WorkflowProgressManager.reCombinateSortAttribute(
   *              sortAttribute, searchForm.getSortOrder(), "A");
   *       call this method in the if (searchForm.isSortable()) statements.
   * @param sortAttribute the sortAttribute in the jsp page.
   * @param sortOrder sortOrder.
   * @param shortTableName the short name of table.
   * @return sort SQL statement.
   */
  public static String reCombinateSortAttribute(String sortAttribute,
           String sortOrder, String shortTableName) {
    String[] tempSortFields = sortAttribute.split(",");
    String newSortAttribute = "";
    String prefix = "";
    String suffix = "";
    if (DataSourceFactory.DB_ORACLE == DataSourceFactory.getDatabaseType()) {
      prefix = "UPPER(";
      suffix = ")";
    }
    for (int x = 0; x < tempSortFields.length; x++) {
      String tempSortField = tempSortFields[x];
      if (tempSortField.indexOf(".") < 0) {
        tempSortField = shortTableName + tempSortField;
      }
      tempSortField = prefix + tempSortField + suffix;
      if (x == tempSortFields.length -1) {
        newSortAttribute = newSortAttribute + tempSortField;
      } else {
        newSortAttribute = newSortAttribute + tempSortField + " "+ sortOrder + ", ";
      }
    }
    return newSortAttribute;
  }
  
}

